SS: Python's alike range. The semantic should be exactly the same (and seems the more sensible for Tcl also).
Code:
# RangeLen(start, end, step) # 1. if step = 0 # 2. then ERROR # 3. if start = end # 4. then return 0 # 5. if step > 0 AND start > end # 6. then ERROR # 7. if setp < 0 AND end > start # 8. then ERROR # 9. return 1+((ABS(end-start)-1)/ABS(step)) proc rangeLen {start end step} { if {$step == 0} {return -1} if {$start == $end} {return 0} if {$step > 0 && $start > $end} {return -1} if {$step < 0 && $end > $start} {return -1} expr {1+((abs($end-$start)-1)/abs($step))} } # Range(start, end, step) # 1. result <- EMPTY LIST # 2. len <- RangeLen(start, end, step) # 3. for i <- 0 to len - 1 # 4. result.append(start+(i*step)) # 6. return result proc range args { # Check arity set l [llength $args] if {$l == 1} { set start 0 set step 1 set end [lindex $args 0] } elseif {$l == 2} { set step 1 foreach {start end} $args break } elseif {$l == 3} { foreach {start end step} $args break } else { error {wrong # of args: should be "range ?start? end ?step?"} } # Generate the range set rlen [rangeLen $start $end $step] if {$rlen == -1} { error {invalid (infinite?) range specified} } set result {} for {set i 0} {$i < $rlen} {incr i} { lappend result [expr {$start+($i*$step)}] } return $result } # Test + Examples: catch {console show} puts [range 10] ;# => 0 1 2 3 4 5 6 7 8 9 puts [range 10 20] ;# => 10 11 12 13 14 15 16 17 18 19 puts [range 10 20 2] ;# => 10 12 14 16 18 puts [range 20 10 -2] ;# => 20 18 16 14 12
I think the Tcl core should include the range command as proposed in my reference implementation (that's semantically equivalent to Python's) for the following reasons:
I'm ready to implement and TIP-fy it, but it's probably best to "sense" the Tclers ideas before to go forward. So, comments are very welcomed.
SS 23Oct2004: An implementation of this can be found at [L1 ]. Apply against HEAD.
RS: Here's a minimal variant that only does step-width 1:
proc range {from to} { if {$to>$from} {concat [range $from [incr to -1]] $to} } % range 0 5 0 1 2 3 4 % range 1 6 1 2 3 4 5
DKF: Extending is easy.
proc range {from to {step 1}} { if {$to>$from} {concat $from [range [incr from $step] $to $step]} }
however this is inefficient. Better to do this:
proc range {from to {step 1}} { set res $from; while {$to>$from} {lappend res [incr from $step]}; return $res }
or even this, adapting to work with negative steps...
proc range {from to {step 1}} { set res $from while {$step>0?$to>$from:$to<$from} {lappend res [incr from $step]} return $res }
glennj This implementation matches the Python 2.6.2 version [L2 ]
proc range args { foreach {start stop step} [switch -exact -- [llength $args] { 1 {concat 0 $args 1} 2 {concat $args 1} 3 {concat $args } default {error {wrong # of args: should be "range ?start? stop ?step?"}} }] break if {$step == 0} {error "cannot create a range when step == 0"} set range [list] while {$step > 0 ? $start < $stop : $stop < $start} { lappend range $start incr start $step } return $range } range 10 ;# ==> 0 1 2 3 4 5 6 7 8 9 range 1 11 ;# ==> 1 2 3 4 5 6 7 8 9 10 range 0 30 5 ;# ==> 0 5 10 15 20 25 range 0 10 3 ;# ==> 0 3 6 9 range 0 -10 -1 ;# ==> 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 range 0 ;# ==> empty range 1 0 ;# ==> empty
caroline I would like a range-generator in a new Tcl version, too. Meanwhile:
1) Make a massive integer-collection that suits your usage:
set number_list "" for {set N 0} {$N <= 10000} {incr N} { lappend number_list $N }
2) ..then just query that list whenever you need a range:
set range_2_to_14 [lrange $number_list 2 14]
LV When this topic first arose some time ago, the Python function was described to me as one in which the numbers were only generated as needed. That way, if one specified a range of a million, one got them, but didn't wait around for them or carry the penalty of the long list. That delayed generation of value functionality, generalized beyond integers, seems like something more useful for Tcl. It might be used for floats, alphas, etc. CL notes that Python has both range() and xrange(); only the latter is lazy.
SS: TIP 192 (lazy lists) covers exactly this aspect.
Sarnold: foriter - a loop command is also lazy, and saves 20% against for. Is someone interested having it into the core ?
foriter i $start $end $step {...}
is the lazy version of:
foreach i [range $start $end $step] {...}
See also Integer range generator, makeranges