'''[http://www.tcl.tk/man/tcl/TclCmd/lmap.htm%|%lmap]''', a [Tcl Commands%|%built-in] Tcl command, iteratively assigns elements of one or more lists to variables, executes a script, and collects the results into a new list. New in [Changes in Tcl/Tk 8.6%|%8.6]. ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/lmap.htm%|%official reference]: [TIP] [http://tip.tcl.tk/405.html%|%405], Add Collecting Loops, the 'lmap' and 'dict map' Commands: ** Synopsis ** : '''lmap''' ''varname list body'' : '''lmap''' ''varlist1 list1'' ?''varlist2 list2 ...''? ''body'' ** Description ** '''`lmap`''' iterates over the values in one or more a lists. At each iteration, it assigns values in the lists to variables as specified by ''varname'' or ''varlist'', executes ''body'', and collects the results into a new list. In the simplest case there is one ''varname'' and one ''list'', but in general, multiplie lists can be specified, along with multiple variable names for each list. When a ''varlist'' contains more than one value, at each iteration the number of values in that ''varlist'' are extracted from the corresponding ''list'', making it possible to iterate through groups of values in the lists rather than iterating individually through each list value. `lmap` iterates over the given lists until all values have been extracted. When the given lists are not of the same length, `lmap` iterates over the longest list, assigning the [empty string] to variables when there is nothing left to extract from the relevant list. Likewise, when there are not enough values remaining in a list to satisfy the associated ''varlist'', the remaining variables in ''varlist'' are filled in with the [empty string]. `[break]` and `[continue]` may be invoked inside ''body'', and, as in `[for]`, have the effect of short-circuiting the iteration, resulting in no value being accumulated into the result for the iteration. In the case of `[break]`, no more iterations are performed and `lmap` returns whatever has been accumulated up until that point. [AMG]: Does the 8.6 `lmap` `[catch]` `[return]` calls, or will a `[return]` cause the caller of `[lmap]` to return? [AMG]: I just checked... the latter is the case. ** `lmap` as a Filter ** [HaO] 2013-11-25: To get only the elements which fulfill a certain condition, one may use: ====== lmap v $list { if {!} continue set v } ====== For example to get all numbers: ======none % lmap v {1 2 3 a b c} { if {![string is entier $v]} continue set v } 1 2 3 ====== ** An Earlier Version of `lmap` in Tcl ** [Richard Suchenwirth] 2005-04-02: `lmap` is a "collecting `[foreach]`" which returns a list of its results. In [Jim] it is built in, but it can be easily had in pure Tcl: ====== proc lmap {_var list body} { upvar 1 $_var var set res {} foreach var $list {lappend res [uplevel 1 $body]} set res } ====== Several usage examples are at [Multiplication tables]. `lmap` is a compromise between Tcl and the classical [functional programming] ''map'' function, in that it takes a "quasi-[lambda]" which is split up into the ''_var'' name and the ''body'' arguments. However, this style is well-known from `[foreach]`, and somehow reads better: ====== lmap i {1 2 3 4 5} {expr {$i * $i}} ====== vs. ====== map [lambda i {expr {$i*$i}}] {1 2 3 4 5} ====== ** Discussion ** [Jim]'s `lmap` uses `[continue]` to skip the accumulation of the current iteration, so it works like `[map]` and `[filter]` at the same time. In Jim, `lmap`, like `[foreach]`, supports multiple lists in input, so you can do interesting things like this: ====== . lmap a {1 2 3} b {A B C} {list $a $b} {1 A} {2 B} {3 C} ====== Multiple lists + accumulation + continue to skip makes it also somewhat similar to list comprehension (but simpler to use in the my (SS) opinion). ---- A cute variation is ''fmap'' (influenced by ApplyAll in Backus' FP; [Joy] has a comparable operator in ''cleave'') which maps a list of functions on one argument: ====== proc fmap {functions x} {lmap f $functions {$f $x}} ====== Then we can write a file reader like this: ====== proc << filename {lindex [fmap {read close} [open $filename]] 0} ====== [NEM]: I like that one! There is an `mmap` function that I wrote with [Monadic TOOT] which is similar to `lmap` (look about 1/3 way down that page). Instead of using `[continue]`, it uses a maybe monad to decide which results to accumulate. (Actually, it's quite general, and any monad could be used). ---- [iu2] 2009-10-15: I like to eliminate the "helper variables": ====== proc lmap {list body} { upvar 1 0 var ;# $0 will be available automatically! set res {} foreach var $list {lappend res [uplevel 1 $body]} set res } ====== So we can write ====== lmap {1 2 3 4 5} {expr {$0 * $0}} ====== As I often replace the original list variable with the mapped list, instead of ====== set list [lmap $list {expr {$0 * $0}}] ====== I can do ====== lmap! list {expr $0 * $0} ====== where lmap! is ====== proc lmap! {listvar body} { upvar 1 $listvar res set res [lmap $res[set res {}] $body] } ====== Finally, the current Tcl constructs are just fine for a one liner ====== set list2 {} foreach x $list { lappend list2 [expr {$x * $x}] } ====== ---- [PL]: an `lmap` implementation in Tcl that allows multiple ''varname''--''listval'' pairs: ====== 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 {varname listval} $args { upvar 1 $varname var$n lappend pairs var$n $listval incr n } set temp [list] foreach {*}$pairs { lappend temp [uplevel 1 $body] } set temp } ====== The above command uses the `[{*}]` [bracestarbrace%|%thingy%|%], so it's still no good for Tcl 8.4 users: one more implementation is perhaps needed. ====== 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 {varname listval} $args { upvar 1 $varname var$n lappend pairs var$n $listval incr n } set temp [list] eval foreach $pairs [list { lappend temp [uplevel 1 $body] }] set temp } ====== ** See Also ** [lcomp]: ** Historical ** The contents in this section do not apply to modern versions of Tcl, but are retained here for the history. ---- [DKF]: Note that `lmap` imposes quite a cost: ====== % time {lmap i {1 2 3 4 5} {expr {$i*$i}}} 100000 24.6734621 microseconds per iteration % time {set res {}; foreach i {1 2 3 4 5} {lappend res [expr {$i*$i}]};set res} 100000 7.2637871 microseconds per iteration % time {apply {l {set res {}; foreach i $l {lappend res [expr {$i*$i}]};set res}} {1 2 3 4 5}} 100000 2.75048408 microseconds per iteration ====== So... `lmap` is 9 times slower than inlining it (the use of `[apply]` shows that the effect of compilation of `[foreach]` is a fair part of it, but that cuts both ways). [DKF]: Note that there's a [http://tip.tcl.tk/405.html%|%proposal] to implement this (in a manner much like the [Jim] version), which will not incur the performance cost mentioned above. (The cost was largely due to the fact that using a procedure like that defeated efficient handling of variables, together with some overhead due to stack frame handling.) [PYK] 2014-06-30: These days, `lmap` is significantly faster: ======none % time {lmap i {1 2 3 4 5} {expr {$i*$i}}} 100000 3.85421 microseconds per iteration % time {set res {}; foreach i {1 2 3 4 5} {lappend res [expr {$i*$i}]};set res} 100000 5.27972 microseconds per iteration % time {apply {l {set res {}; foreach i $l {lappend res [expr {$i*$i}]};set res}} {1 2 3 4 5}} 100000 1.90479 microseconds per iteration ====== ---- [Googie] 2012-10-03: As this command is currently being implemented for 8.6 I feel a need to express my concern. When I first saw [lmap] being mentioned my first thought was "oh, the [string map] for lists, great!", which is a wrong interpretation given what actually the command does. I think that more people will catch themselves with the same impression. I really think that the functionality of this command is expressed much better by '''`lapply`''' name, wouldn't you agree? I know it's a little late for changing any plans for 8.6, but still - worth of consideration. Same applies for proposed `[dict map]` -> '''dict apply'''. [DKF]: We can't make everyone happy, and this page indicates that majority opinion is that it's a code-driven transformation. <> Command | Functional Programming