'''[http://www.tcl.tk/man/tcl/TclCmd/for.htm%|%for]''', a [Tcl Commands%|%built-in] [Tcl] command, provides an iterative loop construct. ** See Also ** [break]: [continue]: [expr]: [foreach]: [if]: [while]: [fold] / [map] / [zip] etc: [for in%|%for ... in]: ** Synopsis ** '''for''' ''start test next body'' ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/for.htm%|%official reference]: ** Description ** '''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 [expr%|%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]` ** `[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. In other configurations, `for` is faster than `foreach`. Consider this example: ====== set myData {2 2 3.5 4.3 5.1 6.7 7.88 8.3 9.01 10.001 11.2 12.46 13.76 14.33321 15.1 16.0 17 18 19 20} puts foreach:[time { set i 0 foreach v $myData { incr i set y [expr {$i*$v}] } } 1000] puts for:[time { for {set i 0} {$i < [llength $myData]} {incr i} { set v [lindex $myData $i] set y [expr {$i*$v}] } } 1000] puts forOptimized:[time { for {set i 0} {$i < [llength $myData]} {incr i} { set y [expr {$i*[lindex $myData $i]}] } } 1000] ====== The result is: foreach:9.469541 microseconds per iteration for:8.148083 microseconds per iteration forOptimized:6.676333 microseconds per iteration So, it depends on the code inside the loop that comes on top of the pure indexing of the element. ---- [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 $result ====== Better 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 $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`/`[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 `[foreach]`es 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 [http://www-db.in.tum.de/~grust/files/monad-comprehensions.pdf%|%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]` ** 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 ** 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"} ;#RS ====== Note 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` ** [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 [https://groups.google.com/d/msg/comp.lang.tcl/GHxR1W_p7-Y/s1Q_h2BzDUwJ%|%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. [Lisp%|%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` ** 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 < $size ====== would 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 } ====== [WJG] (20/08/2023) I was delighted to find that I could effectively combine two for-loops into one; e.g., ====== for { set F $f ; set R $r } { $F <= 8 && $R >= 1 } { incr F ; incr R -1} { lappend buff [lindex $FILES $F]$R } ====== <> Arts and Crafts of Tcl-Tk Programming | Tcl syntax help | Command | Control Structure