A/AK: Since I've liked the sugar macroprocessor and decided to use it constantly, I tried to implement a syntax macro that would expand {*} for pre-8.5 Tcl's in a forward-compatible way.
The ease of implementing it with sugar was a great surprise for me. Feel free to use the code below (don't forget to install sugar before testing).
# Provides {*} support in sugar::proc's for pre-8.5 Tcl
package require sugar 0.1
# Let's test if we already have {*}
if {[[format catch] {list {*}list}]} {
# Tcl can't expand, so we define a syntax macro that will
# replace commands with {*}ed arguments with eval
sugar::syntaxmacro expand-8.5 args {
# The first thing we check: is there anything to expand?
if {[lsearch $args {{\*}?*}]==-1} {
# and if there is none...
return $args
} else {
# We turn the whole command into 'eval'
set evalCmd eval
foreach token $args {
# If the arg is expanded
if {[string match {{\*}?*} $token]} {
# we put [lrange] to the eval's argument list
# to (1) imitate {*}'s pure-list semantics,
# (2) probably gain some speed on pure-list eval
set whattoexpand [string range $token 3 end]
lappend evalCmd "\[lrange $whattoexpand 0 end\]"
} else {
# we append a one-element [list]
# to the eval's argument list
lappend evalCmd "\[list $token\]"
}
}
return $evalCmd
}
}
}
if 1 {
# Simple proc that takes a list value and appends other args twice
sugar::proc testingExpand {lst args} {
lappend lst {*}$args
lappend lst {*}$args
}
# Just to compare: the pre-8.5 traditional naive approach to 'eval'
sugar::proc testingNoExpand {lst args} {
eval lappend lst $args
eval lappend lst $args
}
# Let's test it
puts "Your tcl is [info patchlevel]."
puts "Testing the proc that uses {*} (real or emulated)"
puts [time {testingExpand {a b c} d e f} 20000]
puts "Testing the proc using a 'naive' eval"
puts [time {testingNoExpand {a b c} d e f} 20000]
}After preparing this "toy", "prototype" version of a macro I wanted to know whether it is usable for real life - is there any performance hit introduced by placing literal arguments for eval into list and by placing pure-list expanded arguments into lrange.
Timing results were again of a great surprise:
Your tcl is 8.4.9.
Testing the proc that uses {*} (real or emulated)
29 microseconds per iteration
Testing the proc using a 'naive' eval
36 microseconds per iterationWe'd actually gained run-time performance from using {*} in Tcl 8.4! Though it is emulated with sugar, it's a little faster than a straightforward approach to eval (of course, the compile time will increase much).
Of course I do know that eval runs faster with pure-list arguments, but it was rather unobvious to me that performance may increase either by replacing $args with [lrange $args 0] (when $args is always a pure list already) or by replacing lappend with [list lappend] (seems less probably that it's the case here. Someone interested may test it).
Conclusion: though more performance testing is needed, it seems that with sugar, we may use Tcl-8.5's {*} in Tcl 8.4 without any runtime overhead.
glennj You would get better performance out of the naive eval proc with:
sugar::proc testingNoExpand2 {lst args} {
eval [linsert $args 0 lappend lst]
eval [linsert $args 0 lappend lst]
}My results:
% time {testingNoExpand {a b c} d e f} 20000
18.4102 microseconds per iteration
% time {testingNoExpand2 {a b c} d e f} 20000
10.3229 microseconds per iterationA/AK It's exactly what I don't call naive eval. The macro above expands into eval [lrange ...] [list...] which allows pure-list optimization and is almost as good as using linsert (I've not used linsert in macro expansion because it breaks expected order of parameter substitution).
There is still a lot of code using the eval commandName $args style, which slows the thing down (or used to slow them down) because it doesn't trigger pure-list optimization.
All this thing is not too useful now, as more and more people are migrating to 8.5. The reason to create this page was that I seldom see script-only forward compatibility tricks not leading to slowdown (or leading to speedup).
2009-10-12 I edited the script to provide {*} instead of {expand}, as in the final 8.5 syntax. It was {expand} only on TCL 8.5 alphas.
Here are the timings for current Tcl's CVS HEAD, where the native {*} is used (these results are close to what I've expected):
Your tcl is 8.5a4.
Testing the proc that uses {*} (real or emulated)
13.03415 microseconds per iteration
Testing the proc using a 'naive' eval
33.1472 microseconds per iterationAMG: Heh, we don't have syntax! :^)
AH: We do now. :D
TP: Minor fix for the current sugar version found on the Sugar page.