Version 37 of foreach

Updated 2009-01-17 00:40:39 by andy

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.

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

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

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
[AMG]: This is like the '''do {...} while (0);''' idiom in [C].

----
[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.



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

See also: