Version 5 of for in

Updated 2012-10-19 22:29:08 by pooryorick

This is a drop in replacement for the standard for command. It supports an expanded syntax for looping over iterators. the syntax is

for {varname1 ...} in iterator_name1 [{varname {varnameX ... } in iterator_nameX] ... script 

like foreach does with list, it can operate on multiple iterators and take an arbitrary number of items each time from each iterator. Also like foreach, empty strings are produced as necessary for any iterator which becomes depleted before the others.

rename ::for for_core
interp alias {} ::for {} [namespace current]::for

proc for args {
    if {[lindex $args 1] eq "in"} {
        if {[llength $args] % 3 != 1} {
            return -code error "wrong number of arguments for [info level 0]"
        }
        set assigns {} 
        set assign_template {
            if {[namespace which $iter] eq {}} {
                variable $varname {}
            } else {
                variable $varname [$iter]
            }
        }

        set conditions {}
        set condition_template {[namespace which $iter] ne {}}
        while {[llength $args] > 1} {
            set args [lassign $args[unset args] varnames {} iter]
            foreach varname $varnames {
                lappend assigns [string map [list \$iter [list $iter] \$varname [list $varname]] \
                    $assign_template] 
            }
            lappend conditions [string map [list \$iter [list $iter]] $condition_template]
        }
        set assigns [join $assigns \n]
        set args [join $args \n]
        uplevel "while {[join $conditions { || }]} {$assigns\n$args}"
    } else {
        uplevel [list [namespace current]::for_core {*}$args]
    }
}

example

set result [list]
${iter}::for {key val} in [coroutine students apply {{} {
        yield [info coroutine]
        set db {
                Jack 97 
                Sally 89
                Bob 83
                Jill 77
                John 72
        }
        set index -1 
        foreach {name score} $db {
                yield $name
                yield $score
        }
        return -code break
}}] prize in [coroutine prizes apply {{} {
        yield [info coroutine]
        foreach item { first second third } {
                yield $item
        }
}}] {
        lappend result $key $val $prize
}