foreach

Difference between version 59 and 60 - Previous - Next
'''[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.

<<categories>> Syntax | Arts and Crafts of Tcl-Tk Programming | Command | Control Structure