Version 22 of for

Updated 2006-06-12 17:13:17 by NEM

for - "For" loop

for start test next body for start test next body http://www.purl.org/tcl/home/man/tcl8.4/TclCmd/for.htm

For is a looping command, similar in structure to the C for statement. The start, next, and body arguments must be Tcl command strings, and test is an expression string. The for command first invokes the Tcl interpreter to execute start. Then it repeatedly evaluates test as an expression; if the result is non-zero it invokes the Tcl interpreter on body, then invokes the Tcl interpreter on next, then repeats the loop. The command terminates when test evaluates to 0. If a continue command is invoked within body then any remaining commands in the current execution of body are skipped; processing continues by invoking the Tcl interpreter on next, then evaluating test, and so on. If a break command is invoked within body or next, then the for command will return immediately. The operation of break and continue are similar to the corresponding statements in C. For returns an empty string.

official reference

Note: test should almost always be enclosed in braces. If not, variable substitutions will be made before the for command starts executing, which means that variable changes made by the loop body will not be considered in the expression. This is likely to result in an infinite loop. If test is enclosed in braces, variable substitutions are delayed until the expression is evaluated (before each loop iteration), so changes in the variables will be visible. For an example, try the following script with and without the braces around $x<10:

 for {set x 0} {$x<10} {incr x} {
   puts "x is $x"
 }

(From: TclHelp) See also: TclHelp


The bracing of test will result in expr parsing the contents. But in braced expressions, expr does not tolerate operators to be passed in as variables. If you want to do that, call an explicit expr without braced condition (but inside brackets and braces, as explained above), so the Tcl parser substitutes that in each loop repetition:

 set op "<"
 for {set x 0} {[expr $x $op 10]} {incr x} {puts "x is $x"} ;#RS

In some situations, using a foreach with a fixed list is more convenient than a for, compare:

 foreach i {1 2 3 4 5} {...
 for {set i 1} {$i <= 5} {incr i} {...

or, instead of unrolling a range, you can wrap one for for sugaring:

 proc range {from "to:" to} {
        set res [list]
        for {set i $from} {$i<=$to} {incr i} {lappend res $i}
        set res
 }
 foreach i [range 1 .. 5] {...

This is, however, slower than for.

Foreach is generally better style in non-numeric looping. Those accustomed to writing in C sometimes find this a difficult habit to learn, because they think of an indexed array where a Tcl coder writes simply

 set color_list "red blue umber mauve"
 foreach color $color_list {...}

AMG: Sometimes I need to chew through a list and conditionally edit, replace, or remove elements. I can easily read "copies" of each element in the list using [foreach], but when the condition triggers I need the list index for [lreplace], [lset], [linsert], and the like. What's the best way to handle this? Option the first:

 set list {0 1 2 3 4 5 6}
 set index 0
 foreach elem $list {
     if {$elem % 2 == 0} {
         lset list $index [expr {-$elem}]
     }
     incr index
 }

Option the second:

 set list {0 1 2 3 4 5 6}
 for {set index 0} {$index < [llength $list]} {incr index} {
     set elem [lindex $list $index]
     if {$elem % 2 == 0} {
         lset list $index [expr {-$elem}]
     }     
 }



 set list {0 1 2 3 4 5 6}
 set result [list]
 foreach elem $list {
     if {$elem % 2 == 0} {
         lappend result [expr {-$elem}]
     } else {
         lappend result $elem
     }
 }
 set list $result

Better still would be a list comprehension (e.g., [lcomp]), but to me it seems so pricy that I usually avoid it. Better still would be a list comprehension (e.g., lcomp), but to me it Lars H: Good question; I often find myself wondering the same thing. I suspect that rebuilding the list is on the whole the best approach, when possible (I think dragging the index around is sometimes unavoidable). An nice thing about the rebuild method is that it allows for more functional ways of writing the body; in this case the extreme is probably

 set list {0 1 2 3 4 5 6}
 set result [list]
 foreach elem $list {
     lappend result [expr {
         $elem % 2 == 0 ? -$elem : $elem
     }]
 }
 set list $result

AMG: If your code is functional, does that mean mine is dysfunctional? :^) Seriously, we should look into list comprehensions a bit more. Just how much do they cost, when implemented in Tcl or C? Can something be done in C to allow for a better Tcl implementation? How do list comprehensions compare to writing the equivalent [for]s/[foreach]es? Which is more readable and maintainable? How can things be improved? And so on. AMG: If your code is functional, does that mean mine is dysfunctional? :^) With [lcomp], the above is written:

 set list [lcomp {[expr {$x * -1 ** (1 - $x % 2)}]} x {0 1 2 3 4 5 6}]

Yeah, maybe that exponentiation is going overboard, but I couldn't help myself. :^) Yeah, maybe that exponentiation is going overboard, but I couldn't help myself. Lars H: If you rewrite that expression using the ?: operation, you'll see that my code was just an unrolled version of your lcomp. The last three examples thus demonstrate the variation from imperative style to purely functional style. lcomp can be modified to eval its first argument, perhaps in a [lcomp] can be modified to [eval] its first argument, perhaps in a child interpreter with an [emit] command. That would allow the above to be written in a hybrid functional-imperative notation:

 set list [lcomp {
     if {$x % 2 == 0} {
         emit [expr {-$x}]
     } else {
         emit $x
     }
 } x {0 1 2 3 4 5 6}]

Plus, if [emit] can take multiple arguments and/or can be called multiple times, this syntax allows for single input elements to result in any number (including zero) of output elements. Hmm. Plus, if emit can take multiple arguments and/or can be called Hey, joyous idea. The first argument gets passed unmodified to [eval] (or [interp eval]), which should (??) allow Tcl to bytecode compile it. Bonus! But the nests of [foreach]es would still be pieced together every time [lcomp] is called, so don't put [lcomp] in a loop. Hey, joyous idea. The first argument gets passed unmodified to eval (This is topical because it's an attempt to find alternatives to [for] for list processing. But we might want to move to another page before long. Hey, another idea: dictionary comprehensions!) (This is topical because it's an attempt to find alternatives to for for NEM 12 June 2006: List comprehensions can be generalised to allow queries over a range of different data structures. At the end of this road are things like monad comprehensions [L1 ], and Microsoft's new LINQ framework for .NET. NEM 2006-06-12: List comprehensions can be generalised to allow queries over

 proc map {cmd mylist} {
     set r ""
     foreach e $mylist {lappend r [$cmd $e]}
     set r
 }
 proc negate-even {x} {expr {$x%2==0 ? -$x : $x}}
 map negate-even {0 1 2 3 4 5 6}

With lambda, it could be even simpler. - RS: lmap is a lambda/map compromise pretty much in Tcl's foreach spirit.


lexfiend 2006-Jun-12 - While most people think of for in terms of C-style integer iteration or list-walking, DKF provides a neat reminder in [L2 ] that start, test and next could do many other things too. The code fragment in question:

 for {fconfigure $pipe -eofchar \u0000} {

} {seek $pipe 1 current} {

 } {seek $pipe 1 current} {

}

 }

 fconfigure $pipe -eofchar \u0000
 while {1} {
    # Read up to the next eof char, as configured
     set record [read $pipe]
    if {[string length $record]} {
       # process $record here
    } else {
       break
    }
    # skip over the eof char
    seek $pipe 1 current
 }

Of course, the downside to using this sort of reductionist construct is that it can sometimes be hard to be sure your logic is correct. 8-) - RS: The flexibility of the three first arguments to for was directly inherited from C, where you have the same liberties.


See also:


[ Tcl syntax help | Arts and Crafts of Tcl-Tk Programming | Category Command | Category Control Structure ]