Passing list to Tcl procedure
Asked Answered
E

3

17

What is the canonical way to pass a list to a Tcl procedure?

I'd really like it if I could get it so that a list is automatically expanded into a variable number of arguments.

So that something like:

set a {b c}
myprocedure option1 option2 $a

and

myprocedure option1 option2 b c

are equivalent.

I am sure I saw this before, but I can't find it anywhere online. Any help (and code) to make both cases equivalent would be appreciated.

Is this considered a standard Tcl convention. Or am I even barking up the wrong tree?

Effie answered 20/10, 2009 at 19:11 Comment(1)
Ca you add the word "unwrap" (best as in "unwrap arguments") somewhere in your great question? I had a hard time finding it :-)Quinidine
M
23

It depends on the version of Tcl you're using, but: For 8.5:

set mylist {a b c}
myprocedure option1 option2 {*}$mylist

For 8.4 and below:

set mylist {a b c}
eval myprocedure option1 option2 $mylist
# or, if option1 and 2 are variables
eval myprocedure [list $option1] [list $option2] $mylist
# or, as Bryan prefers
eval myprocedure \$option1 \$option2 $mylist
Mihe answered 20/10, 2009 at 19:35 Comment(5)
... though I personally find \$option1 \$option2 more preferable to [list $option1] [list $optio2]. It more closely shows intent -- your intent isn't to create one-element lists, your intent is to prevent (or protect against) an extra round of substitution for those variables.Tacket
The canonical way to do this for 8.4 and below is: eval [linsert $mylist 0 myprocedure options1 option2]Bayard
I don't agree that's canonical (but maybe we have different canons?). It's arguably safest but it's less readable than \$option1 \$option2 and again somewhat obscures what you're actually trying to accomplish.Tacket
It should be noted that the expansion syntax of 8.5 (and later) is strongly preferred as even seasoned experts used to get into trouble with doing things safely before that. It's also potentially faster.Exurb
Did Tcl8.5 borrow the asterisk (*) from Python or vice versa?Quinidine
B
0

To expand on RHSeeger's answer, you would code myprocedure with the special args argument like this:

proc myprocedure {opt1 opt2 args} {
    puts "opt1=$opt1"
    puts "opt2=$opt2"
    puts "args=[list $args]" ;# just use [list] for output formatting
    puts "args has [llength $args] elements"
}
Bayard answered 21/10, 2009 at 0:31 Comment(1)
I disagree that you'd necessarily code myprocedure to take a variable number of arguments (ie, use "args"). Whether you do so depends entirely on whether or not you need it to take a variable number of inputs, not on how one particular caller happens to have it's inputs available to it.Mihe
J
0

It might be useful to note that passing your command to catch will also solve this problem:

set a {b c}
if [catch "myprocedure option1 option2 $a"] {
    # handle errors
}

This should probably only be used if you want to handle errors in myprocedure at this point in your code so that you don't have to worry about rethrowing any errors that get caught.

Julian answered 15/5, 2015 at 2:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.