MC12/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.)

