Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does {*} do in TCL?

Tags:

list

tcl

I have used some TCL, but this construction stumps me.

When $res = "Table does not exist", what will the following return?

[list [list {*}$res]]

I know what [list [list $res]] would do, but the extra {*} just baffles me.

Thanks for helping.

like image 727
Noah Avatar asked Feb 26 '11 00:02

Noah


3 Answers

When $res = "Table does not exist", what will the following return?

[list [list {*}$res]]

Well, first know that [list {*}…] is a construct that returns a list of the words in the ellipsis (the contents of the res variable in your case). It happens that in your case, the net effect is nothing as the input string is actually also a well-formed list. That then becomes a single argument to the outer list and so we get a single-element list as result, whose element contains a list of the words Table, does, not and exist in that order, i.e., {Table does not exist}.

Notes on expansion

The list of expanded word form is useful for doing concatenation of lists; the concat command does something similar (but not identical; there are some historical weirdnesses involved in the concat command). Thus, you'd concatenate two lists like this:

set concatenation [list {*}$list1 {*}$list2]

Also note that expansion (introduced in Tcl 8.5) is true syntax, which is a very unusual thing in Tcl. The {*} changes the nature of the following substitution so that it yields multiple words instead of just one. While it is possible to do without it, it's actually remarkably difficult to get right. For example, without it the above would be:

set concatenation [eval [linsert $list1 0 list] [lrange $list2 0 end]]

The introduction of expansion has greatly reduced the number of calls to eval required in most Tcl code (a benefit since it was hard to write properly; a lot of programmers were caught out by the difficulty). This has proved particularly beneficial in practice with the exec command; it makes working with glob and auto_execok much easier:

exec {*}[auto_execok $someCmd] $arg1 {*}[glob *.foo]
# Instead of:
#eval [linsert [auto_execok $someCmd] 0 exec] [linsert [glob *.foo] 0 $arg1]
# Or this _wrong_ version:
#eval exec [auto_execok $someCmd] $arg1 [glob *.foo]

Ugh. That last one was a bit brain-bending to write out in non-expansion form even though I know what I'm doing. (The wrong version is wrong because it falls apart if $arg1 contains Tcl meta-characters…)

like image 189
Donal Fellows Avatar answered Oct 14 '22 20:10

Donal Fellows


It's documented on the Tcl syntax manual page. It's dicussed on the Tcl wiki. It was introduced into the language in TIP 293 (its predecessor was TIP 157 where you can learn how it works).

Essentially, {*}$res will split the string into its whitespace-separated words. So, [list {*}$res] acts just like [split $res] (in this case).

The end result is a list with one element, the list of the words in $res.

like image 26
glenn jackman Avatar answered Oct 14 '22 20:10

glenn jackman


Yeah I always find that construction troublesome too. It used be called expand and then they cleverly renamed it to {*} (very memorable!). Anyway I've seen it used to expand a list to make the list contents available.

See this example for an idea of how it works:

% set c [list a b]
a b
% set d [list e f]
e f
% set x [list $c {*}$d]
{a b} e f
% set y [lindex $x 2]
f
% set y [lindex $x 1]
e
% set y [lindex $x 0]
a b
like image 33
TrojanName Avatar answered Oct 14 '22 21:10

TrojanName