FSharp Functional Programming—List Comprehensions
| CSharp-Online.NET:Articles |
| .NET Articles |
| © 2007 Robert Pickering |
List Comprehensions
List comprehensions make creating and converting collections easy. You can create F# lists, sequences, and arrays directly using comprehension syntax. (I cover arrays in more detail in the next chapter, and sequences are collections of type seq, which is F#’s name for the .NET BCL’s IEnumerable type; I describe them in the section "Lazy Evaluation".)
The simplest comprehensions specify ranges, where you write the first item you want, either a number or a letter, followed by two periods (..) and then the last item you want, all within square brackets (to create a list) or braces (to create a sequence). The compiler then does the work of calculating all the items in the collection, taking the first number and incrementing it by 1, or similarly with characters, until it reaches the last item specified. The following example demonstrates how to create a list of numbers from 0 through 9 and a sequence of the characters from A through Z:
#light let numericList = [ 0 .. 9 ] let alpherSeq = { 'A' .. 'Z' } printfn "%A" numericList printfn "%A" alpherSeq
The results of this example are as follows:
[0; 1; 2; 3; 4; 5; 6; 7; 8; 9]
seq ['A'; 'B'; 'C'; 'D'; ...]
To create more interesting collections, you can also specify a step size for incrementing numbers—note that characters do not support this type of list comprehension. You place the step size between the first and last items separated by an extra pair of periods (..). The next example shows a list containing multiples of 3, followed by a list that counts backward from 9 to 0:
#light let multiplesOfThree = [ 0 .. 3 .. 30 ] let revNumericSeq = [ 9 .. -1 .. 0 ] printfn "%A" multiplesOfThree printfn "%A" revNumericSeq
The results of this example are as follows:
[0; 3; 6; 9; 12; 15; 18; 21; 24; 27; 30]
[9; 8; 7; 6; 5; 4; 3; 2; 1; 0]
List comprehensions also allow loops to create a collection from another collection. The idea is that you enumerate the old collection, transform each of its items, and place any generated items in the new collection. To specify such a loop, use the keyword for, followed by an identifier, followed by the keyword in, at the beginning of the list comprehension. In the next example, you create a sequence of the squares of the first ten positive integers. You use for to enumerate the collection 1 .. 10, assigning each item in turn to the identifier x. You then use the identifier x to calculate the new item, in this case multiplying x by itself to square it.
#light let squares = { for x in 1 .. 10 -> x * x } print_any squares
The results of this example are as follows:
seq [1; 4; 9; 16; ...]
You can also add a when guard to suppress items you don’t want in the collection. A when guard is the keyword when followed by a Boolean expression. Only when the when guard evaluates to true will an item be placed in the collection. The next example demonstrates how to use a when guard, checking whether the modulus of x is zero. This is an easy way to check whether a number is even, and only if the number is even is it placed in the collection. The example also demonstrates how to create a function that returns a sequence based on a list comprehension. In the function evens, the parameter n specifies the size of the collection to be generated.
#light let evens n = { for x in 1 .. n when x % 2 = 0 -> x } print_any (evens 10)
The results of this example are as follows:
seq [2; 4; 6; 8; ...]
It’s also possible to use list comprehensions to iterate in two or more dimensions by using a separate loop for each dimension. In the next example, you define a function, squarePoints, that creates a sequence of points forming a square grid, each point represented by a tuple of two integers.
#light let squarePoints n = { for x in 1 .. n for y in 1 .. n -> x,y } print_any (squarePoints 3)
The results of this example are as follows:
[(1, 1); (1, 2); (1, 3); (2, 1); ...]
|

