''Python can now do it, oh why oh why can't tcl?'' List Comprehensions is a short hand way to construct lists from other lists. From '''Haskell: The Craft of Functional Programming''' by Simon Thompson: ''In a list comprehension we write down a description of a list in terms of the elements of another list. From the first list we generate elements, which we test and transform to form elements of the result.'' [Playing Haskell] has an interesting Tcl take on list comprehensions. Here is another: # Synopsis: # listc expression vars1 <- list1 [.. varsN <- listN] [condition]. # proc listc {expression var1 <- list1 args} { set res {} # Conditional expression (if not supplied) is always 'true'. # set condition {expr 1} # We should at least have one var/list pair. # lappend var_list_pairs $var1 $list1 # Collect any additional var/list pairs. # while {[llength $args] >= 3} { lappend var_list_pairs [lindex $args 0] # skip "<-" lappend var_list_pairs [lindex $args 2] set args [lrange $args 3 end] } # If there are no more triplets and at least 1 arg left... # if {[llength $args] > 0} { # Grab condition # set condition [lindex $args 0] } # Build the foreach commands (for each var/list pair). # foreach {var list} $var_list_pairs { append foreachs "foreach \{$var\} \{$list\} \{" } # Insert the conditional expression. # append foreachs {if {[eval $condition]} {lappend res [eval $expression]}} # For each foreach, make sure we terminate it with a closing brace. # foreach {var list} $var_list_pairs { append foreachs \} } # Evaluate the foreachs... # eval $foreachs return $res } Here are some examples: set i [list 1 2 3 4 5 6] set l2 [listc {set i} i <- $i] puts "\tA copy of the list: $l2" ''A copy of the list: 1 2 3 4 5 6'' set dbl [listc {expr $n*2} n <- $i] puts "\tDouble values from list: $dbl" ''Double values from list: 2 4 6 8 10 12'' set evn [listc {set i} i <- $i {expr {$i%2 == 0}}] puts "\tOnly even numbers: $evn" ''Only even numbers: 2 4 6'' proc digits {str} { set lstr [split $str ""] return [listc {set d} d <- $lstr {string is digit $d}] } puts "Just digits from (703)-999-0012= [digits (703)-999-0012]" ''Just digits from (703)-999-0012= 7 0 3 9 9 9 0 0 1 2'' set names1 [list Todd Coram Bob Jones Tim Druid] set lf [listc {list "$l,$f"} {f l} <- $names1] puts "From ($names1)\n\tLast,first = $lf" ''From (Todd Coram Bob Jones Tim Druid) Last,first = Coram,Todd Jones,Bob Druid,Tim'' set l3 [listc {set f} {f l} <- $names1 {string match "T*" $f}] puts "From ($names1)\n\tOnly names starting with 't': $l3" ''From (Todd Coram Bob Jones Tim Druid) Only names starting with 't': Todd Tim'' Now, let's get fancy. Given a proc like lisp's setp and a simple lambda: proc lset {vars {values {}}} { if {$values == {}} { foreach var $vars { lappend values [uplevel set $var] } return $values } uplevel [list foreach $vars $values break] return $values } proc lambda {arglst body} { set name [string map {" " _ $ _} [info level 0]] proc $name $arglst $body set name } You can get even more functional: set names2 [list {Todd Coram} {Bob Jones} {Tim Druid}] set lf [listc {[lambda nm {lset {f l} $nm; return "$l,$f"}] $n} n <- $names2] puts "From ($names2)\n\tAnother last,first = $lf" ''From ({Todd Coram} {Bob Jones} {Tim Druid}) Another last,first = Coram,Todd Jones,Bob Druid,Tim'' This listc proc also supports multiple (nesting) list variables: set l4 [listc {list $n1 $n2} n1 <- [list a b c] n2 <- [list 1 2 3]] puts "Create a matrix pairing {a b c} and {1 2 3}: \n\t$l4" ''Create a matrix pairing {a b c} and {1 2 3}: {a 1} {a 2} {a 3} {b 1} {b 2} {b 3} {c 1} {c 2} {c 3}'' -- [Todd Coram] ---- [RS] has this sugary list comprehension: proc all {varName "in" list "where" condition} { upvar 1 $varName var ;# import into local scope set res {} foreach var $list { if {[uplevel 1 expr $condition]} {lappend res $var} } set res } % all i in {1 0 3 -2 4 -4} where {$i>1} 3 4 % all k in {1 0 3 -2 4 -4} where {$k<1} 0 -2 -4 This allows calls somehow resembling [SQL]: % array set income {Jim 1234 Jack 2345 Bill 3456789} % all i in [array names income] where {$income($i) > 2000} Jack Bill See also [Nested-loop join] ---- [DKF]: Ah, so it is a ''striding'' cross between the ''map'' and ''filter'' functional operations? ---- [AMG] I made an alternative, [lcomp], which constructs its command line using a different method. ---- See also [another list comprehension] ---- [Duoas] I very much like [RS]'s '''all''' there. Here is another version which, while a little less pretty, gives you the ability to bind on multiple variables (much like a single-list [foreach]): proc all {varNames "in" list "where" condition} { foreach varName $varNames { upvar 1 $varName $varName } set result {} foreach $varNames $list { if {[uplevel 1 expr $condition]} { set ls [list] foreach varName $varNames { lappend ls [set $varName] } lappend result $ls } } return $result } And a simple example: % array set income {Jim 1234 Jack 3456 Bill 3456789} % all {name salary} in [array get income] where {$salary > 2000} {Jack 3456} {Bill 3456789} Notice how there is nothing wrong with using the same name for the local variable as the external frame's variable when using [upvar]. Also notice that the original example works the same: % all i in [array names income] where {$income($i) > 2000} Jack Bill (I have specifically avoided constructs available only in modern versions of Tcl.) ---- [[ [Category Concept] | [Arts and crafts of Tcl-Tk programming] ]]