Version 2 of Integer range generator

Updated 2004-01-25 22:20:05

if 0 {Richard Suchenwirth 2004-01-25 - For counted loops, Tcl has inherited the for command from C with its lengthy syntax. Python on the other hand provides only a list iterator like Tcl's foreach, and offers an integer range constructor range() to iterate over counted loops:

    range(1,5) -> [1, 2, 3, 4]
    range(4)   -> [0, 1, 2, 3]  

(A similar thing was the index vector generator, iota, in APL). You can also specify the step-width, which might also be negative. This construct comes handy in Tcl too, where we can then choose between

 for {set i 0} {$i < 5} {incr i} {...}
 foreach i [.. 0 5] {...}

I chose the fancy name ".." as suggestive for a range. Here's the code:}

 proc .. {a {b ""} {step 1}} {
    if {$b eq ""} {set b $a; set a 0} ;# argument shift
    if {![string is int $a] || ![string is int $b]} {
        scan $a %c a; scan $b %c b
        incr b $step ;# let character ranges include the last
        set mode %c
    } else {set mode %d}
    set ss [sgn $step]
    if {[sgn [expr {$b - $a}]] == $ss} {
        set res [format $mode $a]
        while {[sgn [expr {$b-$step-$a}]] == $ss} {
            lappend res [format $mode [incr a $step]]
        }
        set res
    } ;# one-armed if: else return empty list
 }

 proc sgn x {expr {$x>0? 1: $x<0? -1: 0}}

if 0 {For testing this, I came up with a cute and tiny asserter/tester routine:}

 proc must {cmd result} {
    if {[set r [uplevel 1 $cmd]] != $result} {
        error "$cmd -> $r, expected $result"
    }
 }

#-- Tests pass silently, but raise an error if expectations are not met:

 must {.. 5}            {0 1 2 3 4}
 must {.. 0 10 3}       {0 3 6 9}
 must {.. -10 -100 -30} {-10 -40 -70}
 must {.. 2 -2 -1}      {2 1 0 -1}
 must {.. 0 0}          {}
 must {.. A D}          {A B C D}
 must {.. z a -1}       {z y x w v u t s r q p o n m l k j i h g f e d c b a}

KPV It would be useful if this function also had the ability to generate alphabet ranges, e.g [.. A D] => {A B C D}. Perl has this capability and it's surprisingly useful. - RS: Your wish is my command :) It didn't take much to change, considering that characters are integers. For convenience I allowed (as your example suggests) that character ranges run up to the last specified character (while for integers, they run until just below the end limit...)


Arts and crafts of Tcl-Tk programming