Updated 2015-06-18 08:24:05 by foo

Originated by Peter Lewerin (content disclaimer) 2014-09-08.

Updated version 2014-09-18, original code by PL, JAL contributed code to allow the command to take lists of variable names instead of just single variable names.
package require Tcl 8.5
proc lmap args {
    set body [lindex $args end]
    set args [lrange $args 0 end-1]
    set n 0
    set pairs [list]
    foreach {varnames listval} $args {
        set varlist [list]
        foreach varname $varnames {
            upvar 1 $varname var$n
            lappend varlist var$n
            incr n
        }
        lappend pairs $varlist $listval
    }
    set temp [list]
    foreach {*}$pairs {
        lappend temp [uplevel 1 $body]
    }
    set temp
}

For Tcl 8.4 users, who lack the {*} construct:
package require Tcl 8.4
proc lmap args {
    set body [lindex $args end]
    set args [lrange $args 0 end-1]
    set n 0
    set pairs [list]
    foreach {varnames listval} $args {
        set varlist [list]
        foreach varname $varnames {
            upvar 1 $varname var$n
            lappend varlist var$n
            incr n
        }
        lappend pairs $varlist $listval
    }
    set temp [list]
    eval foreach $pairs [list {
        lappend temp [uplevel 1 $body]
    }]
    set temp
}

  Adding varlist arguments (fixed)

JAL Nice work above, but there may be something missing. The lmap page mentions that.... "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)."

So for example....
puts [lmap {x y} {x1 y1 x2 y2 x3 y3} {list $x $y}]

The manpage implies to me (unless I'm mistaken) that the result should be:
{x1 y1} {x2 y2} {x3 y3}

Similar to how foreach works with
 foreach {x y} {x1 y1 x2 y2 x3 y3} {
lappend result_l [list $x $y]
}

Here's a fixed (if it was actually broken?) version of the Tcl 8.4 code above.
package require Tcl 8.4
proc lmap args {
set body [lindex $args end]
set args [lrange $args 0 end-1]
set n 0
set pairs [list]
foreach {var_l listval} $args {
set elem_vars [list]
foreach var $var_l {
upvar 1 $var _u_var${n}_$var
lappend elem_vars _u_var${n}_$var
}
lappend pairs $elem_vars $listval
incr n
}
set temp [list]
# puts "pairs are: $pairs"
eval foreach $pairs [list {
lappend temp [uplevel 1 $body]
}]
set temp
}

That should do the trick.

PL I have updated both definitions to conform with this. The changes are basically the same as in your code, I just cleaned up the naming some. Thanks for the heads-up and the code suggestion.