Version 18 of foreach

Updated 2003-12-02 18:29:39

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.4/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.

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)


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
   a slightly faster version is
 foreach a $b b $a break

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 {...}

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] { 
     puts $myFile [list set myArray($key) $value]
 }
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.

 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
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 _ _ {
    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'?
    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
----
[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.

SS: Yep rmax, my past idea evolved almost exactly into a new idea similar

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 argument 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: man foreach does indeed not say that no assignment nor evaluation takes place if the list iterated over is empty - maybe it should...


See also:


[ Tcl syntax help | Arts and Crafts of Tcl-Tk Programming | Category Command ]