F#11 : Sequences

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

//simple sequence
let seq1 = {1 .. 10 }
//simple list, with step value
let seq2 = {1 ..2..10 }
//simple list, with step value, and this time use the "seq" keyword
let seq3 = seq {1 ..2..10 }
//using for loops to create sequences
let seq4  = seq { for i in 1 .. 10 do yield i * i }
//using for loops to create sequences, but use lambda
let seq5  = seq { for i in 1 .. 10 -> i * i }
//how about creating 2d sequences
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

//yield sequence
let yieldBangSeq = seq { for i in 0..10..100 do
yield! seq {i..1..i*5}}

//yield sequence
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:

//yield sequence
let yieldBangSeq = seq { for i in 0..10..100 do
yield! seq {i..1..i*5}} |> Seq.take 3 |> Seq.toList

//yield sequence
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:

// Recursive isprime function.
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 }

// Compare two sequences element by element.
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.

2 thoughts on “F#11 : Sequences”

1. Alexandre Konioukhov says:

in Creating Sequences you sometimes use word List, where actually should be Sequence,

2. Alexandre Konioukhov says:

here:

let yieldSeq = seq { for i in 0..10..100 do
yield seq {i..1..i*5}} |> Seq.take 3

you forgot Seq.toList