List comprehensions: Difference between revisions
(→{{header|Python}}: removed the generator function example as it is not a comprehension and is syntactically not close.) |
(add Tcl) |
||
Line 115: | Line 115: | ||
<lang python>((x,y,z) for x in xrange(1,n+1) for y in xrange(x,n+1) for z in xrange(y,n+1) if x**2 + y**2 == z**2)</lang> |
<lang python>((x,y,z) for x in xrange(1,n+1) for y in xrange(x,n+1) for z in xrange(y,n+1) if x**2 + y**2 == z**2)</lang> |
||
=={{header|Tcl}}== |
|||
Tcl does not have list comprehensions built-in to the language, but they can be constructed. |
|||
<lang tcl>package require Tcl 8.5 |
|||
# from http://wiki.tcl.tk/12574 |
|||
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] |
|||
{*}$script ;# Change to "eval $script" if using Tcl 8.4 or older. |
|||
return $result |
|||
} |
|||
set range {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} |
|||
puts [lcomp {$x $y $z} x $range y $range z $range {$x < $y && $x**2 + $y**2 == $z**2}]</lang> |
|||
<pre>{3 4 5} {5 12 13} {6 8 10} {8 15 17} {9 12 15} {12 16 20}</pre> |
Revision as of 11:16, 7 May 2009
You are encouraged to solve this task according to the task description, using any language you may know.
A list comprehension is a special syntax in some programming languages to describe lists. It is similar to the way mathematicians describe sets, with a set comprehension, hence the name.
Some attributes of a list comprehension are that:
- They should be distinct from (nested) for loops within the syntax of the language.
- They should return either a list or an iterator (something that returns successive members of a collection, in order).
- The syntax has parts corresponding to that of set-builder notation.
Write a list comprehension that builds the list of all Pythagorean triples with elements between 1 and n. If the language has multiple ways for expressing such a construct (for example, direct list comprehensions and generators), write one example for each.
ALGOL 68
ALGOL 68 does not have list comprehension, however it is sometimes reasonably generous about where a flex array is declared. And with the addition of an append operator "+:=" for lists they can be similarly manipulated.
<lang algol>MODE XYZ = STRUCT(INT x,y,z);
OP +:= = (REF FLEX[]XYZ lhs, XYZ rhs)FLEX[]XYZ: (
[UPB lhs+1]XYZ out; out[:UPB lhs] := lhs; out[UPB out] := rhs; lhs := out
);
INT n = 20; print (([]XYZ(
FLEX[0]XYZ xyz; FOR x TO n DO FOR y FROM x+1 TO n DO FOR z FROM y+1 TO n DO IF x*x + y*y = z*z THEN xyz +:= XYZ(x,y,z) FI OD OD OD; xyz), new line
))</lang> Output:
+3 +4 +5 +5 +12 +13 +6 +8 +10 +8 +15 +17 +9 +12 +15 +12 +16 +20
Clojure
(for [x (range 1 21) y (range x 21) z (range y 21) :when (= (+ (* x x) (* y y)) (* z z))] [x y z])
Common Lisp
Common Lisp doesn't have list comprehensions built in, but we can implement them easily with the help of the iterate
package.
<lang lisp>(defun nest (l)
(if (cdr l) `(,@(car l) ,(nest (cdr l))) (car l)))
(defun desugar-listc-form (form)
(if (string= (car form) 'for) `(iter ,form) form))
(defmacro listc (expr &body (form . forms) &aux (outer (gensym)))
(nest `((iter ,outer ,form) ,@(mapcar #'desugar-listc-form forms) (in ,outer (collect ,expr)))))</lang>
We can then define a function to compute Pythagorean triples as follows:
<lang lisp>(defun pythagorean-triples (n)
(listc (list x y z) (for x from 1 to n) (for y from x to n) (for z from y to n) (when (= (+ (expt x 2) (expt y 2)) (expt z 2)))))</lang>
E
pragma.enable("accumulator") # considered experimental accum [] for x in 1..n { for y in x..n { for z in y..n { if (x**2 + y**2 <=> z**2) { _.with([x,y,z]) } } } }
Erlang
pythag(N) -> [ {A,B,C} || A <- lists:seq(1,N), B <- lists:seq(1,N), C <- lists:seq(1,N), A+B+C =< N, A*A+B*B == C*C ].
Haskell
pyth n = [(x,y,z) | x <- [1..n], y <- [x..n], z <- [y..n], x^2 + y^2 == z^2]
Since lists are Monads, one can alternatively also use the do-notation (which is practical if the comprehension is large):
import Control.Monad pyth n = do x <- [1..n] y <- [x..n] z <- [y..n] guard $ x^2 + y^2 == z^2 return (x,y,z)
Mathematica
Select[Tuples[Range[n], 3], #1[[1]]^2 + #1[[2]]^2 == #1[[3]]^2 &]
Python
List comprehension:
<lang python>[(x,y,z) for x in xrange(1,n+1) for y in xrange(x,n+1) for z in xrange(y,n+1) if x**2 + y**2 == z**2]</lang>
A Python generator comprehension (note the outer round brackets), returns an iterator over the same result rather than an explicit list:
<lang python>((x,y,z) for x in xrange(1,n+1) for y in xrange(x,n+1) for z in xrange(y,n+1) if x**2 + y**2 == z**2)</lang>
Tcl
Tcl does not have list comprehensions built-in to the language, but they can be constructed. <lang tcl>package require Tcl 8.5
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] {*}$script ;# Change to "eval $script" if using Tcl 8.4 or older. return $result
}
set range {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20} puts [lcomp {$x $y $z} x $range y $range z $range {$x < $y && $x**2 + $y**2 == $z**2}]</lang>
{3 4 5} {5 12 13} {6 8 10} {8 15 17} {9 12 15} {12 16 20}