List comprehension
A list comprehension is a syntactic construct available in some programming languages for creating a list based on existing lists. It follows the form of the mathematical set-builder notation as distinct from the use of map and filter functions.
Overview
Consider the following example in set-builder notation.or often
This can be read, " is the set of all numbers "2 times " SUCH THAT is an ELEMENT or MEMBER of the set of natural numbers, AND squared is greater than."
The smallest natural number, x = 1, fails to satisfy the condition x2>3 so 2 ·1 is not included in S. The next natural number, 2, does satisfy the condition as does every other natural number. Thus x consists of 2, 3, 4, 5... Since the set S consists of all numbers "2 times x" it is given by S =. S is, in other words, the set of all even numbers greater than 2.
In this annotated version of the example:
- is the variable representing members of an input set.
- represents the input set, which in this example is the set of natural numbers
- is a predicate expression acting as a filter on members of the input set.
- is an output expression producing members of the new set from members of the input set that satisfy the predicate expression.
- braces indicate that the result is a set
- the vertical bar is read as "SUCH THAT". The bar and the colon ":" are used interchangeably.
- commas separate the predicates and can be read as "AND".
- A variable representing members of an input list.
- An input list.
- An optional predicate expression.
- And an output expression producing members of the output list from members of the input iterable that satisfy the predicate.
In Haskell's list comprehension syntax, this set-builder construct would be written similarly, as:
s = , x^2 > 3 ]
Here, the list
represents, x^2>3
represents the predicate, and 2*x
represents the output expression.List comprehensions give results in a defined order ; and list comprehensions may generate the members of a list in order, rather than produce the entirety of the list thus allowing, for example, the previous Haskell definition of the members of an infinite list.
History
The existence of related constructs predates the use of the term "List Comprehension". The SETL programming language has a set formation construct which is similar to list comprehensions. E.g., this code prints all prime numbers from 2 to N:print;
The computer algebra system AXIOM has a similar construct that processes streams.
The first use of the term "comprehension" for such constructs was in Rod Burstall and John Darlington's description of their functional programming language NPL from 1977. In his retrospective "Some History of Functional Programming Languages", David Turner recalls:
quote|text=I initially called these ZF expressions, a reference to Zermelo-Frankel set theory — it was Phil Wadler who coined the better term list comprehension.
Examples in different programming languages
Similar constructs
Monad comprehension
In Haskell, a monad comprehension is a generalization of the list comprehension to other monads in functional programming.Set comprehension
Version 3.x and 2.7 of the Python language introduces syntax for set comprehensions. Similar in form to list comprehensions, set comprehensions generate Python sets instead of lists.>>> s =
>>> type
>>>
Racket set comprehensions generate Racket sets instead of lists.
v))
Dictionary comprehension
Version 3.x and 2.7 of the Python language introduced a new syntax for dictionary comprehensions, similar in form to list comprehensions but which generate Python instead of lists.>>> s =
>>> s
>>>
Racket hash table comprehensions generate Racket hash tables.
]
#:unless )
)
Parallel list comprehension
The Glasgow Haskell Compiler has an extension called parallel list comprehension that permits multiple independent branches of qualifiers within the list comprehension syntax.Whereas qualifiers separated by commas are dependent, qualifier branches separated by pipes are evaluated in parallel.
-- regular list comprehension
a = , y <-
-- parallel list comprehension
c = | y <-
Racket's comprehensions standard library contains parallel and nested versions of its comprehensions, distinguished by "for" vs "for*" in the name. For example, the vector comprehensions "for/vector" and "for*/vector" create vectors by parallel versus nested iteration over sequences. The following is Racket code for the Haskell list comprehension examples.
> ]
In Julia, practically the same results can be achieved as follows:
- regular array comprehension
- parallel/zipped array comprehension
with the only difference that instead of lists, in Julia, we have arrays.
XQuery and XPath
Like the original NPL use, these are fundamentally database access languages.This makes the comprehension concept more important, because it is computationally infeasible to retrieve the entire list and operate on it.
In XPath, the expression:
/library/book//paragraph
is conceptually evaluated as a series of "steps" where each step produces a list and the next step applies a filter function to each element in the previous step's output.
In XQuery, full XPath is available, but FLWOR statements are also used, which is a more powerful comprehension construct.
for $b in //book
where $b
order by $b//title
return
Here the XPath //book is evaluated to create a sequence ; the where clause is a functional "filter", the order by sorts the result, and the XML snippet is actually an anonymous function that builds/transforms XML for each element in the sequence using the 'map' approach found in other functional languages.
So, in another functional language the above FLWOR statement may be implemented like this:
map, newXML)
filter,
xpath
)
)
LINQ in C#
3.0 has a group of related features called LINQ, which defines a set of query operators for manipulating object enumerations.var s = Enumerable.Range.Where.Select;
It also offers an alternative comprehension syntax, reminiscent of SQL:
var s = from x in Enumerable.Range where x*x > 3 select x*2;
LINQ provides a capability over typical List Comprehension implementations. When the root object of the comprehension implements the IQueryable interface, rather than just executing the chained methods of the comprehension, the entire sequence of commands are converted into an Abstract Syntax Tree object, which is passed to the IQueryable object to interpret and execute.
This allows, amongst other things, for the IQueryable to
- rewrite an incompatible or inefficient comprehension
- translate the AST into another query language for execution
C++
- include
- include
- include
template
C comprehend
int main
There is some effort in providing C++ with list-comprehension constructs/syntax similar to the set builder notation.
- In Boost. Range library there is a notion of adaptors that can be applied to any range and do filtering, transformation etc. With this library, the original Haskell example would look like :
- This implementation uses a macro and overloads the << operator. It evaluates any expression valid inside an 'if', and any variable name may be chosen. It's not threadsafe, however. Usage example:
list
for
N.push_back;
S << list_comprehension
- This implementation provides head/tail slicing using classes and operator overloading, and the | operator for filtering lists. Usage example:
bool x2
list
int x, y;
for
l.push_back;
= l | x2;
= t;
t = l < 9;
t = t < 7 | even | x2;
- Language for Embedded Query and Traversal is an embedded DSL in C++ that implements X-Path-like queries using operator overloading. The queries are executed on richly typed xml trees obtained using xml-to-c++ binding from an XSD. There is absolutely no string encoding. Even the names of the xml tags are classes and therefore, there is no way for typos. If a LEESA expression forms an incorrect path that does not exist in the data model, the C++ compiler will reject the code.
...
LEESA provides >> for X-Path's / separator. X-Path's // separator that "skips" intermediate nodes in the tree is implemented in LEESA using what's known as Strategic Programming. In the example below, catalog_, book_, author_, and name_ are instances of catalog, book, author, and name classes, respectively.
// Equivalent X-Path: "catalog/book/author/name"
std::vector
evaluate;
// Equivalent X-Path: "catalog//name"
std::vector
evaluate;
// Equivalent X-Path: "catalog//author"
std::vector
evaluate
>> Select
>> name_);
Haskell
- The Haskell 98 Report, chapter .
- The Glorious Glasgow Haskell Compilation System User's Guide, chapter .
- The Hugs 98 User's Guide, chapter .
OCaml
-
Python
- The Python Tutorial, .
- Python Language Reference, .
- Python Enhancement Proposal .
- Python Language Reference, .
- Python Enhancement Proposal .
Common Lisp
- by Guy Lapalme
Clojure
-
Axiom