foreach , a built-in Tcl command, iterates over all elements in one or more lists and evaluates a script at each iteration
foreach implements a loop where the loop variable(s) take on values from [foreach] implements a loop where the loop variable(s) take on values from varname, and one list, listn, and with each iteration, the next item in list is assigned to varname. body is a Tcl script that is evaluated once for each iteration of the loop.
Although any of the lists passed as arguments can be modified by body, the changes will not be seen by foreach as the loop iterations continue. changes will not be seen by [foreach] as the loop iterations continue. preserve this behaviour. If a list is large, modifying it within body can therefore have an adverse impact on memory.
To take multiple items from the list at each iteration, supply multiple variable names in varlist:
foreach {name gender} [list Bob male Joe male Sarah female Kim unknown] { foreach {name gender} [list Bob male Joe male Sarah female Kim hermaphrodite] { }
Multiple lists can be traversed in simultaneously by adding them as additional arguments to the command. During each iteration of the loop, subsequent items from each list are assigned to the corresponding variables. Items in each list are used in order from first to last, and each item is used exactly once.
$ tclsh % foreach a [list 1 2 3 4] b [list 5 6 7 8] c [list a b c d] d [list w x y z] { puts "$a $b $c $d" } 1 5 a w 2 6 b x 3 7 c y 4 8 d z %
Combining multiple variable names and multiple lists:
foreach {fname lname} [join $first_and_last_names] phone $phone_numbers { # code block }
The total number of loop iterations is large enough to use up all the values from all the lists. If a list does not contain enough items for each of its loop variables in each iteration, empty values are used for the missing elements:
% unset -nocomplain a b c % foreach {a b c} {1 2} {break} % info exists c 1
break and continue may be invoked inside body, and have the [break] and [continue] may be invoked inside body, and have the same effect as in [for]. [foreach] returns an empty string. (From:
If all the list arguments are empty lists, no assignment to the corresponding variables occurs, and body is never evaluated:
% unset -nocomplain a b c % foreach {a b c} {} {break;} % info exists a 0
The documentation could be interpreted as implying that the corresponding variables would receive empty values:
but then it goes on to dispel that interpretation, since zero iterations is large enough to iterate over zero items:
set l [list a bc def 1 23 456] set m 0 foreach i $l { incr m puts "member $m is $i" }
output:
member 1 is a member 2 is bc member 3 is def member 4 is 1 member 5 is 23 member 6 is 456
To stride through a list, picking up every n'th item, a dummy variable like $- can be used:
foreach {- i} $list { do something with $i while ignoring $- }
To save an array to a file:
foreach {key value} [array get myArray] { puts $myFile [list set myArray($key) $value] }
Of course, you could have written:
puts $myFile [list array set myArray [array get myArray]]
but when editing such a file, the lack of line endings might give your text editor indigestion.
To save an array to a file, ordering it alphabetically by key:
foreach {key} [lsort -dictionary [array names myArray]] { puts $myFile [list set myArray($key) $myArray($key)] }
In a canvas, applying [foreach] to the result of the [bbox] is often the idea.
grid [canvas .c -width 400 -height 300 -bg gray85] set i [.c create text 200 50 -text "One" -anchor n] foreach item { Two Three Four } { foreach { - - - y } [.c bbox $i] break set nexty [expr { $y + 50 }] .c create line 200 $y 200 $nexty -arrow last set i [.c create text 200 $nexty -text $item -anchor n] }
KPV: A similar example illustrates finding the dimensions of a canvas to shrink the size to provide a margin around the layout:
# Get canvas dimensions shrunk by some foreach who {x0 y0 x1 y1} val [.c cget -scrollregion] d {30 30 -30 -30} { set $who [expr {$val + $d}] }
One popular idiom is to use the varlist assignments to split up a list. The body in such cases would just be break to exit foreach on the first body in such cases would just be [break] to exit [foreach] on the first
foreach {first second third} $list break ;# RS
A similar pattern can be used to swap variable values:
foreach {a b} [list $b $a] break
It might be tempting to abbreviate the above to foreach a $b b $a break, but don't do that; it really sets b to the result of [lindex $a 0] and don't do that; it really sets b to the result of [lindex $a 0] and isn't it will result in shimmering.
AMG: lassign can do both these tasks. AMG: [lassign] can do both these tasks.
lassign $list first second third lassign [list $b $a] a b
Unlike foreach, it returns any unused elements from its first argument. Unlike [foreach], it returns any unused elements from its first argument. stack. Also unlike foreach, it won't create variables having an empty stack. Also unlike [foreach], it won't create variables having an empty it useful for detecting insufficient arguments.
When using incr in combination with foreach to get the index of When using [incr] in combination with [foreach] to get the index of of 0, and incr at the beginning of the script rather than at the end, of 0, and [incr] at the beginning of the script rather than at the end, so that [continue] can be placed anywhere in the script:
set index -1 foreach item $list { incr index # Now do something using both $index and $item ... if {something} continue ... }
The reason to incr index first in the loop body is that this makes The reason to [incr] index first in the loop body is that this makes [continue] do the right thing.
foreach can be used as a code block that runs exactly once, but [foreach] can be used as a code block that runs that runs exactly once, but can be exited from with [break] or [continue] (seen in a c.l.t post
foreach _ _ { # some processing if {$somecondition} break # some other processing if {$someothercondition} break # still more processing }
This is an alternative to nested if structures. This is an alternative to nested [if] structures. RS is not sure whether to recommend this style.. but proof again that you can do more with Tcl than you'd imagine...;-) Well hell, then add some sugar:
interp alias {} breakable {} foreach _ _ breakable { ;# or 'fragile'? # some processing if {$somecondition} break # some other processing if {$someothercondition} break # still more processing }
Or, to make your intention clearer:
interp alias {} make {} foreach make or break { ... }
AMG: This is like the do {...} while (0); idiom in C, which is useful not only in macros but also for creating a "structured goto" using break. Perhaps unfortunately, continue can't be used to go back to the top, since it first checks the loop condition; otherwise this idiom would effectively create two line labels at the same time. ;^) Very occasionally I wish for a "redo" statement that restarts the current iteration of the loop, bypassing both the loop condition and for's increment clause.
SS wonders why foreach doesn't return the result of the last executed command. It can be useful for programmers using Tcl in a functional style. The same also apply to for. CL likes the idea. same also apply to [for]. CL likes the idea.
rmax: Another useful return value (maybe even closer to functional
programming) could be the unprocessed part of the list if a break occured in the loop body.
I sometimes find myself doing stuff like this:
foreach {a b c} $args break set args [lrange $args 3 end]
This could be done easier and would be less error prone if foreach returned the tail of the list:
set args [foreach {a b c} $args break]
Of course, this feature can only work in a useful way if foreach was called with just one list.
RS: This is what lassign (from 8.5, or TclX) does: RS: This is what [lassign] (from 8.5, or TclX) does:
set args [lassign $args a b c]
SS: Yep rmax, my past idea evolved almost exactly into a new idea similar
to your suggestion. Actually I'm not sure about many different options:
set args [foreach {a b c} $args]
does what your example do.
set args [foreach {a b c} $args]
is still valid.