Sarnold on 2008-05-24: We can have lazy lists in Tcl because we can build new control structures, and here is a package which mimics Python's range() function with Tcl's foreach syntax.
namespace eval ::lazy {
namespace export lazy range
# constructor
proc lazy {initval shift last} {
list $initval $shift $last
}
# gets the first element AND the new list
proc _shift {list} {
set first [lindex $list 0]
lset list 0 [eval [lindex $list 1] [list [lindex $list 0]]]
list $first $list
}
proc shift {listvar} {
upvar $listvar list
::foreach {c list} [_shift $list] break
set c
}
# boolean true if the next element (car) is the last
# does not modify list
proc last {list} {
eval [lindex $list 2] [list [lindex $list 0]]
}
# the new control structure : accepts a lazy list 'list'
# otherwise it behaves like foreach except it does not handle
# multiple list iterations
proc foreach {vars list body} {
set res ""
while {1} {
set last no
::foreach v $vars {
if {$last} {return $res}
set last [last $list]
uplevel 1 [list set $v [shift list]]
}
set res [uplevel 1 $body]
if {$last} {return $res}
}
}
# a range generator
proc range {start end {step 1}} {
set end [expr {$end - $step}]
list $start [list ::lazy::rangeshift $step] [list ::lazy::rangelast $end]
}
proc rangeshift {step value} {
incr value $step
}
proc rangelast {end value} {
expr {$end <=$value}
}
}
# test code
namespace import lazy::*
foreach step {1 2 3} {
set l [range 0 10 $step]
lazy::foreach x $l {puts $x}
}