AMG I wasn't quite satisfied with [listc], Todd Coram's list comprehension procedure, so I adapted it into something far worse. Behold!
proc lcomp {expression args} { # Check the number of arguments. if {[llength $args] < 2} { error "wrong # args: should be \"lcomp expression var1 list1\ ?... varN listN? ?condition?\"" } # Extract condition from $args, or use default. if {[llength $args] % 2 == 1} { set condition [lindex $args end] set args [lrange $args 0 end-1] } else { set condition 1 } # Collect all var/list pairs and store in reverse order. set varlst [list] foreach {var lst} $args { set varlst [concat [list $var] [list $lst] $varlst] } # Actual command to be executed, repeatedly. set script {lappend result [subst $expression]} # If necessary, make $script conditional. if {$condition ne "1"} { set script [list if {[expr $condition]} $script] } # Apply layers of foreach constructs around $script. foreach {var lst} $varlst { set script [list foreach $var $lst $script] } # Do it! set result [list] {expand}$script ;# Change to "eval $script" if using Tcl 8.4 or older. return $result }
Mainly I didn't like [listc]'s method of constructing its $foreachs variable (which I have named $script in my code), so I switched it around to be built inside-out rather than left-to-right. I start by putting together the actual command that'll be run by the tangled nest of [foreach] constructs; then I tack on [foreach]s and [if]s with the aid of [list]. Magically I no longer have to worry about matching braces, thus shortening my code into the sorry heap you see above.
Next up, I changed [eval] to [subst] and [expr], since I felt that all $expressions would use substitution and all $conditions would be boolean expressions. Especially nice is the ability to use [...] substitutions with both [subst] and [expr], so the old behavior can be had by wrapping the parameter with [ and ].
Lastly (unless I missed something), I dropped the <- sugar. I didn't much care for it, since [foreach] itself doesn't use it. You can re-add it if you like by changing "% 2" to "% 3" and "< 2" to "< 3" and the first instance of "var lst" to "var <- lst".
Examples? I'll start by listing Todd's examples modified to work with [lcomp].
set i [list 1 2 3 4 5 6] set l2 [lcomp {$i} i $i] puts "A copy of the list: $l2" set dbl [lcomp {[expr {$n*2}]} n $i] puts "Double values from list: $dbl" set evn [lcomp {$i} i $i {$i%2 == 0}] puts "Only even numbers: $evn" proc digits {str} { set lstr [split $str ""] return [lcomp {$d} d $lstr {[string is digit $d]}] } puts "Just digits from (703)-999-0012= [digits (703)-999-0012]" set names1 [list Todd Coram Bob Jones Tim Druid] set lf [lcomp {$l,$f} {f l} $names1] puts "From ($names1): Last,first = $lf" set l3 [lcomp {$f} {f l} $names1 {[string match "T*" $f]}] puts "From ($names1): Only names starting with 't': $l3" set l4 [lcomp {[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}: $l4"
Here are some more:
lcomp {$x} x {0 1 2 3} ;# 0 1 2 3 lcomp {$y $x} {x y} {0 1 2 3} ;# {1 0} {3 2} lcomp {[expr {$x ** 2}]} x {0 1 2 3} ;# 0 1 4 9 lcomp {[expr {$x + $y}]} x {0 1 2 3} y {0 1 2 3} ;# 0 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 lcomp {$x} x {0 1 2 3} {$x % 2 == 0} ;# 0 2
Practical uses? The following deletes all images named by any elements in the images array:
image delete {expand}[lcomp {$val} {key val} [array get images]]
I'll come up with more later as I use [lcomp] more in my own code.
AMG: It's possible to modify [lcomp] to use [eval] to process $expression rather than [subst] or [expr]. [subst]-like behavior can be had with the [K*] proc found on K. Single-argument [list] or [concat] should also work. For instance,
lcomp {K* $x} x {0 1 2 3} ;# 0 1 2 3 lcomp {concat $y $x} {x y} {0 1 2 3} ;# {1 0} {3 2} lcomp {expr {$x ** 2}} x {0 1 2 3} ;# 0 1 4 9 lcomp {expr {$x + $y}} x {0 1 2 3} y {0 1 2 3} ;# 0 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 lcomp {K* $x} x {0 1 2 3} {$x % 2 == 0} ;# 0 2
For the moment at least, there's a bit of list comprehension discussion at the bottom of [for]. Some day (soon?) those ideas will migrate to this page.
See also [list].
[ Category Command | Category Concept | Arts and crafts of Tcl-Tk programming ]