'''[http://www.tcl.tk/man/tcl/TclCmd/lmap.htm%|%lmap]''', a [Tcl Commands%|%built-in] Tcl command introduced in version [Changes in Tcl/Tk 8.6%|%8.6], iterates through items in one or more lists and processes those items into a new list. ** 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''' ''names list'' ?''names list ...''? ''body'' ** Description ** '''`lmap`''' repeatedly takes enough items are from each ''list'' to store one item in each of the variables named in ''names'', evaluates ''body'' as a script, appends the result to another list, and returns that list once all items in all ''list'' arguments have been used. If any ''list'' is used up before any others, the [empty string] is assigned as needed to the relevant variables. 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]. If the result of any evaluation is `[return%|%break]`, no further iterations are performed and the list that has been accumulated to that point is returned. If the result of an evaluation is `[return%|%continue]`, it is ignored. [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 ====== [PYK] 2016-01-02: And to wrap it into a procedure for a little less typing: ====== proc lwhere {varname list condition} { uplevel [list lmap $varname $list \ "if [list $condition] {set [list $varname]} continue"] } ====== [KPV] 2017-09-20: another example of lmap, this time to find the length of the longest word in a list: ====== set words {now is the time for all good men to come to the aid of their country} set how_long [::tcl::mathfunc::max {*}[lmap v $words {string length $v}]] set longest_word [lindex [lsort -index 1 -integer [lmap v $words { list $v [string length $v] }]] end 0] ====== ** 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}] } ====== ---- An `lmap` implementation in Tcl that allows multiple ''varname''--''listval'' pairs (see also [lmap forward compatibility]): ====== 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]: list comprehensions! [lolcat]: for when you need more than one result per iteration ** Historical ** '''The contents in this section do not apply to 8.6 and later 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