'''[http://www.tcl.tk/man/tcl/TclCmd/foreach.htm%|%foreach]''', a [Tcl Commands%|%built-in] [Tcl] command, iterates over all elements in one or more lists and evaluates a script at each iteration ** Synopsis ** : '''foreach''' ''varlist list body'' : '''foreach''' ''varlist1 list1'' ?''varlist2 list2 ... varlistn listn''? ''body'' ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/foreach.htm%|%official reference documentation]: ** See Also ** [for]: [while]: [foreach little friends]: [http://paste.tclers.tk/3012%|%foreach with coroutines], by [KBK]: [ycl%|%ycl list foreach]: Like `[foreach]`, but accepts names of lists, and consumes items from them. [for in%|%for ... in]: ** Description ** `[foreach]` implements a loop where the loop variable(s) take on values from one or more lists. In the simplest case, there is one loop variable, ''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. Internally, Tcl will make a copy of the list where necessary in order to 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] { ... } ====== 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 same effect as in `[for]`. `[foreach]` returns an empty string. (From: [TclHelp]) 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: : If a value list does not contain enough elements for each of its loop variables in each iteration, empty values are used for the missing elements.'' but then it goes on to dispel that interpretation, since zero iterations is large enough to iterate over zero items: : The total number of loop iterations is large enough to use up all the values from all the value lists. If a value list ... ** Examples ** ====== 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 a convenient way to create canvas layouts. The following script illustrates 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] } ====== [foreachOnbbox] ---- [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}] } ====== [http://img684.imageshack.us/img684/1269/image71.gif] ** `[foreach]` as an enhanced `[set]` ** 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 iteration: ====== 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 vice versa, which in general is something quite different, and even when it isn't it will result in [shimmering]. [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. This makes it useful for detecting extra arguments or popping elements off a stack. Also unlike `[foreach]`, it won't create variables having an empty string value when its first argument doesn't have enough elements. This makes it useful for detecting insufficient arguments. ** Caveat: `[incr]` and `[continue]` ** When using `[incr]` in combination with `[foreach]` to get the index of the item in the list, it's a good idea to set the initial value to `-1` instead 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 `[continue]` do the right thing. ** Shorting Circuits (One-time Loop) ** `[foreach]` can be used as a code block that runs exactly once, but can be exited from with `[break]` or `[continue]` (seen in a c.l.t post by [Bruce Hartweg]): ====== foreach _ _ { # some processing if {$somecondition} break # some other processing if {$someothercondition} break # still more processing } ====== 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. ** Development: What Could `[foreach]` Return? ** [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. ---- [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: ====== 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: OPTION 1: `[foreach]` always returns the return value of the last command executed, but can be called without the 'script' argument, and in such a case the variables are assigned, and foreach returns the rest of the list, so: ====== set args [foreach {a b c} $args] ====== does what your example do. OPTION 2: `[foreach]` always returns the rest of the list (with rest I mean the ''not processed part''). The last argument can be omitted and defaults to `[break]`. ====== set args [foreach {a b c} $args] ====== is still valid. OPTION 3: `[foreach]` works exactly like today, but works as OPTION 1 when called without the script argument. That's fully backward compatible, but less orthogonal and less powerful. ---- [AMG]: [[[lmap]]] works just like [[foreach]] but indeed does have a return value. The returned value is a list accumulating the results of each iteration. <> Syntax | Arts and Crafts of Tcl-Tk Programming | Command | Control Structure