Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / F#

F#11 : Sequences

0.00/5 (No votes)
30 Apr 2014CPOL4 min read 16K  
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 […]

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

image

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:

image

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.

image

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:

image

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">
// 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:

image

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:

image

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:

image

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)