[MC], 31 Dec 2004: In a post on his blog[http://foothills.wjduquette.com/archives/000975.html] discussing implementation and design issues of his [Ramble] game (written, naturally in Tcl/Tk), [WHD] proposes a ''fori'' command, whose signature looks like: '''fori''' ''var'' ''from'' ''to'' ''script'' Since any Tcl version is going to be slower than the built-in version of [[[for]]], Will writes: "''I think something like'' '''fori''' ''would be a nice addition to Tcl...but it would have to be implemented as a C extension.''" [Larry Smith]12/31/04: More than that - you'd need to bytecode it. ''for'' wins out every time because it is already bytecoded and, once compiled, can be run very cheaply, whereas ''fori'' will always involve a procedure call. I also hate the syntax, try: loop -var -from -to <-do> ''script''. You might see how fast it would be if it was a [Sugar] macro. [Lars H], 1 Jan 2005: Another take at this problem can be found in [TIP] #225 [http://tip.tcl.tk/225.html]. The syntax that comes out of that is '''foreach''' var '''[[range''' ''from'' ''to+1'' ''']]''' ''script'' and thanks to some implementation magic, it is actually supposed to be faster than [for]. However the ''to+1'' argument (i.e., you don't specify the end of the range, but the first thing not in the range) seems terribly odd (e.g. not the same as for [lrange] or [string range]). Perhaps a decided opinion against that detail can sway the proposer? [WHD], 1 Jan 2005: I actually tried using foreach and a "range" command, minus the implementation magic; it was no better than any other solution I came up with, and it has a major drawback for nested loops (though possibly this is mitigated by the implementation magic): if you use "range" in an inner loop, it's generating the list over which to iterate once for each iteration of the outer loop. ---- [MC]12/31/04: I was curious how much slower a Tcl version would be... Here are a couple of different approaches I came up with this afternoon: proc fori1 {var m n script} { uplevel 1 for [list "set [list $var] $m"] [list "\$[list $var] <= $n"] [list "incr [list $var]"] [list $script] } proc fori2 {var m n script} { uplevel 1 for [list "set [list $var] $m"] [list "\[set [list $var]\] <= $n"] [list "incr [list $var]"] [list $script] } proc fori3 {var m n script} { uplevel 1 "set [list $var] [expr {$m - 1}] while {\[incr [list $var]\] <= {$n}} { $script }" } proc fori4 {var m n script} { uplevel 1 "if {1} { for {set [list $var] {$m}} {\$[list $var] <= {$n}} {incr [list $var]} { $script } }" } proc fori5 {var m n script} { uplevel 1 "if {1} [list "set [list $var] [expr {$m - 1}] while {\[incr [list $var]\] <= {$n}} { $script }"]" } and now a bit of infrastructure to make testing/timing them easier: proc try {name proc limit {n_iterations 50}} { set time [time {set result [$proc $limit]} $n_iterations] return [format { #%-5s %-14s result == %8d, in %7d microseconds} \ $name "(1 .. $limit):" $result [lindex $time 0]] } proc builtin {limit} { set sum 0 for {set i 1} {$i <= $limit} {incr i} { incr sum $i } return $sum } for {set i 1} {$i <= 5} {incr i} { proc method$i {limit} [format { set sum 0 fori%d i 1 $limit {incr sum $i} return $sum } $i] } foreach size {10 100 1000 10000} { if {$size != 10} then {puts ""} foreach version {builtin method1 method2 method3 method4 method5} \ name {for fori1 fori2 fori3 fori4 fori5} { # run once before we begin timing $version $size puts [try $name $version $size] } } and now to see the timing results (these are from tclsh (8.4.7) on a 1.33GHz PowerBook running [MacOS X] 10.3 via [wiki-reaper] | tclsh): #for (1 .. 10): result == 55, in 57 microseconds #fori1 (1 .. 10): result == 55, in 290 microseconds #fori2 (1 .. 10): result == 55, in 285 microseconds #fori3 (1 .. 10): result == 55, in 218 microseconds #fori4 (1 .. 10): result == 55, in 183 microseconds #fori5 (1 .. 10): result == 55, in 173 microseconds #for (1 .. 100): result == 5050, in 86 microseconds #fori1 (1 .. 100): result == 5050, in 4183 microseconds #fori2 (1 .. 100): result == 5050, in 1373 microseconds #fori3 (1 .. 100): result == 5050, in 3808 microseconds #fori4 (1 .. 100): result == 5050, in 277 microseconds #fori5 (1 .. 100): result == 5050, in 219 microseconds #for (1 .. 1000): result == 500500, in 358 microseconds #fori1 (1 .. 1000): result == 500500, in 7054 microseconds #fori2 (1 .. 1000): result == 500500, in 7087 microseconds #fori3 (1 .. 1000): result == 500500, in 5559 microseconds #fori4 (1 .. 1000): result == 500500, in 3664 microseconds #fori5 (1 .. 1000): result == 500500, in 1553 microseconds #for (1 .. 10000): result == 50005000, in 5034 microseconds #fori1 (1 .. 10000): result == 50005000, in 54393 microseconds #fori2 (1 .. 10000): result == 50005000, in 53805 microseconds #fori3 (1 .. 10000): result == 50005000, in 39142 microseconds #fori4 (1 .. 10000): result == 50005000, in 20296 microseconds #fori5 (1 .. 10000): result == 50005000, in 15092 microseconds ---- Out of these ''fori5'' looks the best overall, but it is still 2.5-5 times slower than [for] is; can we do any better? [DKF]: Curious. Why is ''fori5'' faster than ''fori4''? (Note that ''fori5'' is faster than [TclX]'s [loop] command, but that shouldn't be too surprising given that that uses inefficient interfaces for a number of things.)