What does {*} do in TCL?
Asked Answered
D

3

11

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.

Deutschland answered 26/2, 2011 at 0:28 Comment(0)
N
12

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…)

Nikolai answered 28/2, 2011 at 1:17 Comment(4)
I see you are active on the TCL boards as well. I need to backport this change into a C# version of TCL 8.4 If it is not too much work, can you point me toward the source checkin where this was added.Deutschland
@Noah: The first implementation patch – from November 2003 – is at core.tcl.tk/tcl/info/cbfb8313ba but the implementation of the feature was changed a number of times subsequently (e.g., to use {*} instead of {expand}, and to get rid of bugs). You might need to log into that site (as anonymous) to make the links work.Nikolai
@Noah: I'm reminded by one of the other Tcl devs that there is already a reasonably recent version of Tcl-in-C#: Eagle. (more links at wiki.tcl.tk/6580)Nikolai
.. I downloaded it and sad to say ... % #version Eagle v1.0.4093.38850 beta NetFx20 2011.03.18 (Release) A Tcl 8.4 compatible interpreter for the Common Language Runtime. <snip> % set test [list [list a {b c}]] {a {b c}} % set test [list {*}[list a {b c}]] Error, line 1: extra characters after close-braceDeutschland
L
7

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
Laurenelaurens answered 26/2, 2011 at 1:20 Comment(3)
Any ideas about this? If I have a place to play via the web I could have figured this out. #5124803Deutschland
I know you said there you wanted a web based solution. If you don't find one, try the ActiveTCL activestate.com/activetcl/downloadsLaurenelaurens
To your comment "isn't a list"... remember that there's really no such thing as "is a list" or "isn't a list" in Tcl, as everything is a string. There's only "can be treated as a list" and "cannot be treated as a list". Just the same as the value "1" can be treated as an int... it's not an int, but you can use it as one.Tjirebon
P
7

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.

Penetralia answered 26/2, 2011 at 12:20 Comment(2)
It's not quite the same: % llength [split {a b c {d e}}] 5 % llength [list {*}{a b c {d e}}] 4Deutschland
@noah, yes, true. I suppose {*} was meant to expand lists, not strings. Just happens that the OP's string is a list too.Penetralia

© 2022 - 2024 — McMap. All rights reserved.