Version 9 of range

Updated 2004-10-14 08:57:25

SS: Python's alike range. The semantic should be exactly the same (and seems the more sensible for Tcl also).

Examples:

 range 10                    ;# => 0 1 2 3 4 5 6 7 8 9
 range 10 20                 ;# => 10 11 12 13 14 15 16 17 18 19
 range 10 20 2               ;# => 10 12 14 16 18
 range 20 10 -2              ;# => 20 18 16 14 12

Code:

 # range ?start? end ?step?
 #
 # Python's alike range function.
 # Returns the [start,end) integer range.
 #
 # Example:
 #
 # foreach x [range 0 10] y [range 20 0 -2] {
 #     puts "$x $y"
 # }
 proc range args {
     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?"}
     }
     set result {}
     if {$start <= $end} {
         for {set j $start} {$j < $end} {incr j $step} {
             lappend result $j
         }
     } else {
         for {set j $start} {$j > $end} {incr j $step} {
             lappend result $j
         }
     }
     return $result
 }

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:

  • Sequences of natural numbers are often useful in programming, so the command is of very general use.
  • It may become idiomatic to write: foreach x range 0 10 instead of for {set x 0} {$x < 10} {incr x} for small loops. It's much clear.
  • The Python semantic appears to be the best for languages where indexes are zero-based (Pascal noted it, and I think it's a smart observation).
  • If implemented in a smart way, range may speed-up Tcl loops comprated to for. A smart implementation should cache small ranges and return the shared object for the next call.
  • At some point Tcl may include TIP 192 in order to make ranges used for loops to have a space complexity of O(1).

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.


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
  }

See also Integer range generator


Category Algorithm