Version 45 of foreach

Updated 2012-10-20 18:39:12 by RLE

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)


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.



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.gifgold added pix of KPV's canvas procedure.