Last time we looked at the List module, and we now proceed to look at the sequence module. You will in fact see a lot of similarities with the sequence module compared to the list module. The main difference between F# lists and F# sequence is pretty much the same as it is in C#|VB .NET, A list is every element and is all loaded (greedily) into memory, whist sequences are lazy and will only be evaluated as needed. This is much the same as it is with IEnumerable<T> in standard .NET. In fact sequences are represented by the seq<’T>
type, which is an alias for IEnumerable<T>. Therefore, any .NET Framework type that implements System.IEnumerable can be used as a sequence.
Creating Sequences
In F# there are several ways in which you can create Lists, shown below are a few examples.
- Create a simple list containing 1 to 10
- Create a simple list creating odd numbers between 1 and 10, which is achieved using a step operator
- Using a for loop to create a list
You will definitely see similarities between the code below and the code we saw in the last post when creating lists. In fact what makes a list is the existence of the “[“ and “]” braces, whilst sequence uses “{“ and “}” style braces.
let prettyPrint desc seq =
printfn desc
printfn "%A" seq
let seq1 = {1 .. 10 }
let seq2 = {1 ..2..10 }
let seq3 = seq {1 ..2..10 }
let seq4 = seq { for i in 1 .. 10 do yield i * i }
let seq5 = seq { for i in 1 .. 10 -> i * i }
let Create2dArray (height, width) =
seq { for row in 0 .. width - 1 do
for col in 0 .. height - 1 do
yield (row, col)
}
let seq6 = Create2dArray (5,5)
let seq7 = seq { for i in 1 .. 20 do if i = 10 then yield i }
prettyPrint "let seq1 = {1 .. 10 }" seq1
prettyPrint "let seq2 = {1 ..2..10 }" seq2
prettyPrint "let seq3 = seq {1 ..2..10 }" seq3
prettyPrint "let seq4 = seq { for i in 1 .. 10 do yield i * i }" seq4
prettyPrint "let seq5 = seq { for i in 1 .. 10 -> i * i }" seq5
prettyPrint "let Create2dArray (height, width) =
seq { for row in 0 .. width - 1 do
for col in 0 .. height - 1 do
yield (row, col)
}
let seq6 = Create2dArray (5,5)\r\n" seq6
prettyPrint "let seq7 = seq { for i in 1 .. 20 do if i = 10 then yield i }\r\n" seq7
Which when run will give the following results
Yield!
As well as the Yield
keyword we have already seen, we can also use the yield! keyword, to yield a single outer sequence. This is similar to IEnumerable.SelectMany
. Lets see an example of this is action, which may help you to understand the differences between Yield and Yield! (pronounced yield bang).
Lets see an example of the difference between Yield and Yield!
let prettyPrint desc seq =
printfn desc
printfn "%A" seq
let yieldBangSeq = seq { for i in 0..10..100 do
yield! seq {i..1..i*5}}
let yieldSeq = seq { for i in 0..10..100 do
yield seq {i..1..i*5}}
prettyPrint "let yieldBangSeq = seq { for i in 0..10..100 do
yield! seq {i..1..i*5}}\r\n" yieldBangSeq
printfn "\r\n\r\n"
prettyPrint "let yieldSeq = seq { for i in 0..10..100 do
yield seq {i..1..i*5}}\r\n" yieldSeq
Which when run looks like this:
It can be seen that the example above that uses the Yield! actually only produces one final sequence, whilst the the one that uses Yield produces sequences within sequences.
If we examine the types of the 2 bound values it may help to solidify this a bit further.
See how the Yield! version is a single Seq<int>
, whilst the regular Yield one is a Seq<Seq<int>>
. Lets now turn our attention to what it would look like if we used the results of both of these values. Here is some revised code:
let yieldBangSeq = seq { for i in 0..10..100 do
yield! seq {i..1..i*5}} |> Seq.take 3 |> Seq.toList
let yieldSeq = seq { for i in 0..10..100 do
yield seq {i..1..i*5}} |> Seq.take 3
Which when run will give us the following results:
Consuming A Sequence
It is fairly easy to consume a sequence once you have one, it can be done as simply as follows:
<pre class="brush: csharp; gutter: false; title: ; toolbar: false; notranslate">
let isprime n =
let rec check i =
i > n/2 || (n % i <> 0 && check (i + 1))
check 2
let aSequence = seq { for n in 1..100 do if isprime n then yield n }
for x in aSequence do
printfn "%d" x
Where we can use a simple for loop (this will be covered in a later blog post), to iterate (lazily) over the sequence. Here is the results of running this:
The Sequence Module
As we saw last time with the List module, the Sequence module is also a VERY important module in F#, and just as with the List module, the Sequence module gets some good loving from the Microsoft team. So just as before I will not be able to bring much to the table that they do not already cover. I will cover a few example which as before I borrowed (um stole) from the MSDN web site for the Sequence module which is found at : http://msdn.microsoft.com/en-us/library/dd233209.aspx
There are way too many functions for me to include in this blog. As with the List module MSDN is actually very good for Sequences, which sadly can not be said of all aspects of F# development. For Sequences you are fine though, MSDN is rad and Gnarly.
Some examples
I have included a couple of examples here just so you can get a feel for the Sequence module
Seq.Take / Seq.toList
Here is an example where we take the first 10 elements from a sequence, and then toList
the sequence, which creates a non lazy in memory List.
let prettyPrint desc seq =
printfn desc
printfn "%A" seq
let bigSeq = seq { 1..1000}
let smallerList = bigSeq |> Seq.take 10 |> Seq.toList
prettyPrint "let bigSeq = seq { 1..1000}\r\nlet smallerList = bigSeq |> Seq.take 10 |> Seq.toList" smallerList
Which when run gives the following results:
Seq.iter / Seq.pairwise / Seq.map
Here is an example where we do the following:
- The Seq.pairwise function generates a sequence of tuples of consecutive squares, { (1, 4), (4, 9), (9, 16) ... }
- The seq.map allow us to run a lambda across each element in the sequence
- The Seq.iter allows us to iterate over the sequence an element at a time
let printSeq seq1 = Seq.iter (printf "%A ") seq1;
printfn ""
let seqPairwise = Seq.pairwise (seq { for i in 1 .. 10 -> i*i })
printSeq seqPairwise
printfn ""
let seqDelta = Seq.map (fun elem -> snd elem - fst elem) seqPairwise
printSeq seqDelta
Seq.compareWith
compareWith
allow us to to compare sequences (that is all the elements in a sequence). Here is an example:
let sequence1 = seq { 1 .. 10 }
let sequence2 = seq { 10 .. -1 .. 1 }
let compareSequences = Seq.compareWith (fun elem1 elem2 ->
if elem1 > elem2 then 1
elif elem1 < elem2 then -1
else 0)
let compareResult1 = compareSequences sequence1 sequence2
match compareResult1 with
| 1 -> printfn "Sequence1 is greater than sequence2."
| -1 -> printfn "Sequence1 is less than sequence2."
| 0 -> printfn "Sequence1 is equal to sequence2."
| _ -> failwith("Invalid comparison result.")
Which when run will give the following results:
As I say the Sequence module has so many goodies in it, I could not possible cover them all here. Luckily the Sequence module is very well documented using the MSDN link, so use that it is your friend.