See Also edit
Synopsis edit
for start test next bodyDocumentation edit
Description edit
For is a looping command, similar in structure to the C for statement. The start, next, and body arguments are scripts , and test is an expression. start is evaluated, and if test is true, body is evaluated, after which next is evaluated. After each evaluation of next, test is evaluated, and the cycle continues until test evaluates to false.continue and break function in a manner similar to the corresponding statements in C. If continue is called within body, the evaluation of body terminates immediately and the cycle continues with the evaluation of next, test, and so on. If break is called within body or next, for returns immediately.for returns an empty string.test should almost always be enclosed in braces. Otherwise, substitutions are done once by the interpreter prior to handing the argument to for, and once again when for evaluates it as an expression, in which case any changes made to the variable during the evaluation of body are not detected, resulting in an infinite loop. See double substitution for examples of other possible unintended effects. If, on the other hand, test is enclosed in braces, substitutions are suppressed, leaving expr to do them. 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" }See also: TclHelp
for vs foreach edit
foreach is generally better style for 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 programmer writes:set color_list "red blue umber mauve" foreach color $color_list {...}In some situations, using foreach with a fixed list is more convenient than for, even if the list is numeric. Compare:
foreach i {1 2 3 4 5} {... for {set i 1} {$i <= 5} {incr i} {...
Some find the idea of using foreach on a range of integers so nice that they pour some sugar over for:
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.
The main difference between for and foreach when iterating over a list is that with for, the index of each item is available, whereas with foreach, it is not. foreach can, however, be preferable even if the index is needed. What would traditionally be coded as
for {set index 0} {$index < [llength $list]} {incr index} { set item [lindex $list $index] # Now do something using both $index and $item ... }is actually faster when written as the following foreach:
set index -1 foreach item $list { incr index # Now do something using both $index and $item ... }In order for continue to do the right thing, incr index comes first in the loop body.
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?One option:
set list {0 1 2 3 4 5 6} set index -1 foreach elem $list { incr index if {$elem % 2 == 0} { lset list $index [expr {-$elem}] } }Another option:
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}] } }Which is preferred? Here's another option.
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 $resultBetter still would be a list comprehension (e.g., lcomp), but to me it seems so pricy that I usually avoid 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 $resultAMG: 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/foreach commands? Which is more readable and maintainable? How can things be improved? And so on.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. :^)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 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.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 foreaches would still be pieced together every time lcomp is called, so don't put lcomp in a loop.(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!)NEM 2006-06-12: 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 (Torsten Grust) and Microsoft's new LINQ framework for .NET.
Sarnold finds this quick enough :
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.
wdb: In Python there is only a foreach equivalent, and for-loops are done with ranges such as 4..11.In Tcl, you can define a proc range as follows:
proc range {from {to ""} {step 1}} { if {$to eq ""} then { set to $from set from 0 } set result {} for {set x $from} {$x < $to} {set x [expr {$x + $step}]} { lappend result $x } set result }Now, having this function, you can say:
foreach i [range 4 11] {puts $i}which makes your code shorter (but not faster).Lars H: And places you at the mercy of the endpoint conventions used — which are not the same in this range as in the other range on this page. Murphy strikes again!
for vs while edit
In some languages, the range of a for loop is determined at the start of the loop, and unbounded loops must rather be coded using while. Tcl is not one of those languages (because of C heritage), so for is preferable to while whenever there is a loop variable.One case where this happens is a loop over the elements of a list (queue), where processing one element may cause further elements to be appended to the list:proc graph_component {graph v} { set queue [list $v] for {set n 0} {$n < [llength $queue]} {incr n} { set v [lindex $queue $n] if {[info exists seen($v)]} then {continue} set seen($v) {} lappend queue {*}[dict get $graph $v] } return [array names seen] }graph here is assumed to be a dictionary mapping vertices to their lists of neighbours. v is the vertex whose component one wishes to compute.The corresponding loop with while is less clear:
proc graph_component {graph v} { set queue [list $v] set n 0 while {$n < [llength $queue]} { set v [lindex $queue $n] incr n if {[info exists seen($v)]} then {continue} set seen($v) {} lappend queue {*}[dict get $graph $v] } return [array names seen] }foreach alone wouldn't work, because the queue can grow. A while–foreach combination is possible, but less clear than the simple for:
proc graph_component {graph v} { set queue [list $v] while {[llength $queue]} { set next {} foreach v $queue { if {[info exists seen($v)]} then {continue} set seen($v) {} lappend next {*}[dict get $graph $v] } set queue $next } return [array names seen] }
Dynamic Expressions edit
The bracing of test minimizes interpretation of the value by Tcl, before passing the value to expr. Tcl interpretation can be used, though to generate an expression using a variable whose value is an operator. On way to accomplish that is to call expr from within the expression itself:set op < for {set x 0} {[expr $x $op 10]} {incr x} {puts "x is $x"} ;#RSNote that not bracing the expr expression will mean there are two substitution rounds. This can lead to problems if some substituted value is not a simple number.Alternatively, use quotes and backslashes:
set op < for {set x 0} "\$x $op 10" {incr x} {puts "x is $x"}This substitutes op before calling for, but x only when the expression is evaluated. This technique makes it easier to handle complex substituted values, but the need to escape all $ signs can be a burden in complex expressions.
Making Use of all the Arguments to for edit
lexfiend 2006-06-12: While most people think of for in terms of C-style integer iteration or list-walking, DKF provides a neat reminder in how to gets with an arbitrary "newline" character, comp.lang.tcl, 2006-06-11, that start, test and next could do many other things too. The code fragment in question:for {fconfigure $pipe -eofchar \u0000} { [string length [set record [read $pipe]]] } {seek $pipe 1 current} { # process $record here }is an impressively succinct rendition of the more typical:
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.Lars H: Actually, Tcl gives you greater liberties than C here, because in C the start and next are restricted to being expressions, and expressions in C cannot do any flow control. In a C expression, there's no way, for example, to return or even throw an error, whereas Tcl lets you do these things.aspect: While the for code above suffers in readability, it's heading down a useful path. Lispers would tend to look for an underlying abstraction which is easy in tcl:
proc with-records {eofchar $_record body} { upvar 1 $_record record set _eofchar [fconfigure $pipe -eofchar] for {fconfigure $pipe -eofchar $eofchar} { [string length [set record [read $pipe]]] } {seek $pipe 1 current} { uplevel 1 $body } fconfigure $pipe -eofchar $_eofchar }In writing this construct, the need to reset -eofchar at the end of the look was exposed and easily addressed. Also I'd tend to use the default name $_ for the record instead of having to explicitly provide it, but that's a matter of taste, as is the name of the procedure, which I'm not entirely happy with. But the point is that making new control structures is easy: you don't have to be shoehorn your problem into for, foreach and while like in most other languages.
Tips and Tricks on for edit
A poster to comp.lang.tcl mentioned the following problem. Some code was set up like this:set size [getresult from operation] for {set i 0} {$i<$size} {incr i} { set workingarray($i) 0 }However, due to a mistake, the output from getresult changed at one point from being a number to being a string. Rather than causing for to raise an error, the bug caused the for loop to loop thousands of times until, in the poster's case, memory was used up.While
expr $i < $sizewould raise an error
expr {$i<$size}, as well as the use within the for only returns a 1, causing the loop to run forever.Test your variables to ensure they actually have the type of value you expect, after getting values from a user or external source.A quick way to assert that a value is an integer is to incr it by 0, like so:
set size [getresult from operation] incr size 0 for {set i 0} {$i<$size} {incr i} { set workingarray($i) 0 }