foreach - Iterate over all elements in one or more lists : '''foreach''' ''varname list body'' ** Synopsis ** : '''foreach''' ''varlist1 list1'' ?''varlist2 list2 ...''? ''body'' : '''foreach''' ''varlist list body'' http://purl.org/tcl/home/man/tcl8.5/TclCmd/foreach.htm : '''foreach''' ''varlist1 list1'' ?''varlist2 list2 ... varlistn listn''? ''body'' The '''foreach''' command 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, ''list'', that is a list of values to assign to ''varname''. The ''body'' argument is a Tcl script. For each element of ''list'' (in order from first to last), '''foreach''' assigns the contents of the element to ''varname'' as if the '''[lindex]''' command had been used to extract the element, then calls the Tcl interpreter to execute ''body''. Occasionally developers are surprised to discover that if ''list'' is modified within the confines of the ''body'' 's code, ''varname'' never reflects the change. This is due to the tcl parser having a fixed copy of the elements of ''list'' in memory and not referring to the actual variable during the assignment of values to ''varname'' during the loop construct. In the general case there can be more than one value list (e.g., ''list1'' and ''list2''), and each value list can be associated with a list of loop variables (e.g., ''varlist1'' and ''varlist2''). During each iteration of the loop the variables of each varlist are assigned consecutive values from the corresponding ''list''. Values in each ''list'' are used in order from first to last, and each value is used exactly once. The total number of loop iterations is large enough to use up all the values from all the value lists. 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. [http://www.tcl.tk/man/tcl/TclCmd/foreach.htm%|%official reference documentation]: The '''[break]''' and '''[continue]''' statements may be invoked inside ''body'', with the same effect as in the '''[for]''' command. '''Foreach''' returns an empty string. (From: [TclHelp]) ---- **Examples** foreach {name gender} [list Bob male Joe male Sarah female Kim unknown] { set m 0 foreach i $l { incr m puts "member $m is $i" } ====== produces, as output 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 ====== ---- <> It is a popular [idiom] to use the varlist assignments to split up a list. The body in such cases would just be ''break'' to stop foreach running into a second round: To stride through a list, picking up every n'th item, a dummy variable like `$-` ====== foreach {first second third} $list break ;# RS ====== You might as well swap variables with that: ====== 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 assigned to empty string when its first argument doesn't have enough elements. This makes it useful for detecting insufficient arguments. ---- Get every second element of a list with the following ("-" being a fancy variable name that indicates it will not be used): foreach {- i} $list { foreach {- i} $list {...} To save an array to a file: ---- Foreach is also a convenient way to work with Tcl's associative arrays. For instance, you can save an array to a file as a Tcl script by using: ====== foreach {key value} [array get myArray] { foreach { key value } [array get myArray] { } ====== Of course, you could have written: If you want the keys alphabetized, then ====== foreach { key } [lsort -dictionary [array names myArray]] { puts $myFile [list set myArray($key) $myArray($key)] } ====== ====== puts $myFile [list array set myArray [array get myArray]] ====== but when editing such a file, the lack of line endings might give your text but that style creates scripts which cause trouble in many text editors. To save an array to a file, ordering it alphabetically by key: ---- Applying 'foreach' to the result of the 'bbox' command in a canvas is often a convenient way to create canvas layouts. The following script gives the idea. ====== foreach {key} [lsort -dictionary [array names myArray]] { 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 A similar example is when I wanted to find the dimensions of a canvas but I wanted to shrink the size to provide a margin around the layout. [KPV] ====== # 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] ---- '''One-time loop:''' You can use [foreach] also for a loop that runs exactly once, and you're not interested in the iterator, but get the side effect that you can "jump out" of the body 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. Helps you avoid 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: [RS] is not sure whether to recommend this style.. but proof again that you can 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 [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. [rmax]: Another useful return value (maybe even closer to functional [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. ====== 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 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 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 [SS] Yep [rmax], my past idea evolved almost exactly in what you said. 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: 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]`. 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. 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. <> Syntax | Arts and Crafts of Tcl-Tk Programming | Command | Control Structure ---- [male] 2003-12-02: Is this a bug or a feature? ====== % unset -nocomplain a b c % foreach {a b c} {1 2} {break;} % info exists c 1 ====== or ====== % unset -nocomplain a b c % foreach {a b c} {} {break;} % info exists a 0 ====== I know - an empty list causes no loop! So the variables shouldn't exist, right? But the foreach manual page says something that could be used to argue that the variables a, b and c should exist after the second example: ''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.'' So, since 0 values in the value list certainly is ''not [[...]] enough elements for each of its loop variables'', one wonders if this is an undocumented pre-condition (one or more values must exist), or whether it is a bug. [RS]: A '''man -s n foreach''' indeed does not mention that no assignment nor evaluation takes place if the list to be iterated over is empty - maybe it should... dansic: Yes but the manpage for foreach goes like this: ''The total number of loop iterations is large enough to use up all the values from all the value lists. If a value list ...'' This means that for an empty value list 0 iteration is large enough to use up all the values from the value list and since there is this other sentence from the manpage: ''During each iteration of the loop the variables of each varlist are assigned ...'' Then because there is no iteration then no assignment will occur. ---- [IL]: I just heard the following works which is crazy to me. I had heard you can do a list1, b list 2, but for it to be able to flatten those per iteration is pretty impressive. [LV] Let's try it and see: ====== $ tclsh % foreach a [list a b c d] b [list 1 2 3 4] { puts "a=$a, b=$b" } a=a, b=1 a=b, b=2 a=c, b=3 a=d, b=4 ====== Yup, that indeed works just fine. And in fact, if you read the man page, you will see that form listed. In fact, what might not be obvious from reading the man page: ====== $ 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 % ====== Looking at the examples, there is another form as well - which allows one to assign alternating values from a list to a list of variables. That one is good if you have things in a {keyword value} style list... ====== foreach { fname lname } [join $first_and_last_names] phone $phone_numbers { # code block } ====== ---- [LES] The first paragraph says that [foreach] iterates "''in order from first to last''". But I've always heard that it does NOT follow any order! Indeed, in my tests, [foreach] really seems to follow the list's original order, but very often I don't know whom I should believe. [RS]: [foreach] indeed follows order. You may have confused this with [array names], where the order is often not evident. I usually do (when order matters) ====== foreach key [lsort [array names arr]] {...} ====== [http://img684.imageshack.us/img684/1269/image71.gif][gold] added pix of [KVP]'s canvas <> See also: * [for] * [while] * [foreach little friends] ---- !!!!!! %| [Tcl syntax help] | [Arts and Crafts of Tcl-Tk Programming] | [Category Command] | [Category Control Structure] |% !!!!!!