F# Sequences

In this article, we will take a closer look at F# sequences and how they can be used with examples.

F# Sequences is a data abstraction over a collection, and it allows for efficient, lazy evaluation and composition of data, allowing it to provide fast and efficient access to the data.



Creating F# Sequences

This syntax is used to define sequences in the following way:

seq { expr }
For example,
let seq1 = seq { 1 .. 8 }

Sequences can also be created using ranges and comprehensions, similar to lists.

A sequence expression is an expression which you can use to create sequences from the input data.

You can do these things:

  • This can be done by specifying a range of values.
  • This can be done by specifying a range along with increments or decrements.
  • It is done by using a keyword called yield to produce values that will become part of a sequence after being executed.
  • You can do this by using the ‘‘ operator.

F# Sequences Basic Example

Here is the simple example to understand the concept of sequence:

Example: 

//Sequences let seq1 = seq { 1 .. 8 }//ascending order and increment printfn "The Sequence: %A" seq1 let seq2 = seq { 1 .. 2 .. 20 }//descending order and decrement printfn "The Sequence: %A" seq2let seq3 = seq {20 .. -2 .. 0} printfn "The Sequence: %A" seq3//using yield let seq4 = seq { for a in 1 .. 8 do yield a, a*a, a*a*a } printfn "The Sequence: %A" seq4

And the output will be:

The Sequence: seq [1; 2; 3; 4; ...]
The Sequence: seq [1; 3; 5; 7; ...]
The Sequence: seq [20; 18; 16; 14; ...]
The Sequence: seq [(1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64); ...]

Prime Number Checker

To check the numbers are prime or not in the sequence:

Example: 

//Recursive checkPrime function. let checkPrime n = let rec check i = i > n/2 || (n % i <> 0 & check (i + 1)) check 2let primeIn20 = seq { for n in 1..20 do if checkPrime n then yield n } for x in primeIn20 do printfn "%d" x

The above program results as follows:

1
2
3
5
7
11
13
17
19

F# Sequence Operations

Here is a table showing the basic operations that can be performed on a sequence data type:

ValueOverview
append : seq<‘T> → seq<‘T> → seq<‘T>The function returns a new sequence containing the elements of the first sequence followed by the elements of the second sequence at the end.
average : seq<^T> → ^TThe average element in the sequence of elements is returned as the result of this function.
averageBy : (‘T → ^U) → seq<‘T> → ^UBy applying the function to each element of the sequence and taking the average of both elements, it will return the average of the elements generated.
cache : seq<‘T> → seq<‘T>This method returns a sequence of sequences that correspond to a cached version of the input sequence that has been input.
cast : IEnumerable → seq<‘T>Provides a way to wrap loosely-typed systems. The sequence of collections is typed as a sequence of collections.
choose : (‘T → ‘U option) → seq<‘T> → seq<‘U>The given function will be applied to every element of the sequence one by one. This function will return a sequence of each element’s result for each element where the function returns Some as a result.
collect : (‘T → ‘Collection) → seq<‘T> → seq<‘U>This function will be applied to each element of the sequence , one at a time. Return the combined sequence of all the results after concatenating all of them together.
compareWith : (‘T → ‘T → int) → seq<‘T> → seq<‘T> → intThe function compares two sequences, element by element, using the comparison function that has been given.
concat : seq<‘Collection> → seq<‘T>A new sequence will be returned which will contain the elements of all of the sequences , sorted alphabetically.
countBy : (‘T → ‘Key) → seq<‘T> → seq<‘Key * int>This function generates unique keys for each element of a sequence and returns the number of instances of those keys in the original sequence.
delay : (unit → seq<‘T>) → seq<‘T>Returns a sequence that is created from a sequence specification that has been delayed and the configuration that has been given.
distinct : seq<‘T> → seq<‘T>In this method, the entries are hashed and the equality of the entries is compared to determine whether they are duplicates. The later occurrences of an element are discarded if the element occurs more than once in the sequence.
distinctBy : (‘T → ‘Key) → seq<‘T> → seq<‘T>Based on the general hashing and equality comparison of the keys returned by the key-generating function, this function returns a sequence of entries that do not contain any duplicate entries. When the same element occurs in a sequence multiple times, then the later occurrence of the element is discarded.
empty : seq<‘T>The function returns an empty sequence with the given type as its value.
exactlyOne : seq<‘T> → ‘TThe empty sequence is created as a result of this function.
exists : (‘T → bool) → seq<‘T> → boolThe function checks if any element from the sequence meets the given predicate by checking whether it is present.
exists2 : (‘T1 → ‘T2 → bool) → seq<‘T1> → seq<‘T2> → boolIn this function, it is tested to see if a pair of corresponding elements in the sequences is satisfied by the given predicate.
filter : (‘T → bool) → seq<‘T> → seq<‘T>In this function, a new collection is returned that contains only those elements of the original collection for which the given predicate corresponds to true.
find : (‘T → bool) → seq<‘T> → ‘TIt returns the element that is the first element to return true when the given function is used.
findIndex : (‘T → bool) → seq<‘T> → intA predicate that is satisfied by the first element in the sequence is returned.
fold : (‘State → ‘T → ‘State) → ‘State → seq<‘T> → ‘StateThis function threads an accumulator argument through each element of the collection. It applies the function to the second argument and the first element of the sequence . This result will then be passed by the program along with the second element, and the process will continue in this way. It then returns the final result. A function f is given, and the elements are i0…iN, so this function computes f (… (f s i0) i1 …) iN.
forall : (‘T → bool) → seq<‘T> → boolThis function is used to determine whether or not all elements of the sequence meet the given predicate.
forall2 : (‘T1 → ‘T2 → bool) → seq<‘T1> → seq<‘T2> → boolThe purpose of the test is to see whether all pairs of elements that are drawn from either sequence satisfy the given predicate. The remaining elements of the sequence will be ignored if one of the sequence lengths is shorter than the other.
groupBy : (‘T → ‘Key) → seq<‘T> → seq<‘Key * seq<‘T>>Each element of a sequence is applied to a key-generating function, which yields a sequence of unique keys based on the elements of that sequence. In addition to a unique key, there is also a sequence containing all elements that match the unique key in order to make it unique.
head : seq<‘T> → ‘TIt is used to return the first element in a sequence .
init : int → (int → ‘T) → seq<‘T>The function creates a sequence by using the given generator as an index on each item in the sequence .
initInfinite : (int → ‘T) → seq<‘T>The function returns successive elements when iterated, based on the newly created sequence. A call to the function does not save its results, which means that the function will have to be applied again to regenerate the elements in the future. Using the index, the function can generate an item.
isEmpty : seq<‘T> → boolThis method returns true if the sequence does not contain any elements, and false if it does.
iter : (‘T → unit) → seq<‘T> → unitEvery element in the sequence will be applied with the given function.
iter2 : (‘T1 → ‘T2 → unit) → seq<‘T1> → seq<‘T2> → unitThe given function is applied simultaneously to two sequences. There must be an identical size between the two sequences.
iteri : (int → ‘T → unit) → seq<‘T> → unitEach element in the sequence is applied to the given function. Integers passed to the function indicate element indexes.
last : seq<‘T> → ‘TThe last element of the sequence is returned when this function is called.
length : seq<‘T> → intThe length of the sequence is returned by this function.
map : (‘T → ‘U) → seq<‘T> → seq<‘U>In this method, the elements of a collection are the results of applying a given function to each element of a collection, and the result is returned as the elements of a new collection.
map2 : (‘T1 → ‘T2 → ‘U) → seq<‘T1> → seq<‘T2> → seq<‘U>In this method, a new collection is created whose elements correspond to the respective elements of the two sets of collections if the given function is applied pairwise to the corresponding elements of the two sets.
mapi : (int → ‘T → ‘U) → seq<‘T> → seq<‘U>Applying a given function to each element of the collection creates a new collection whose elements are the results of the application. This integer index represents the element’s index (from 0) when passed to the function.
max : seq<‘T> → ‘TBy using Operators.max, this function returns the greatest value among all of the elements in the sequence.
maxBy : (‘T → ‘U) → seq<‘T> → ‘TThe function returns the greatest element in the sequence among all the elements of the sequence, comparing the result of the function with its Operators.max method.
min : seq<‘T> → ‘TThis function returns the lowest element in a sequence, compared using Operators.min as a comparison method.
minBy : (‘T → ‘U) → seq<‘T> → ‘TUsing Operators.min on the result of the function, this function returns the lowest element within all the elements of the sequence.
nth : int → seq<‘T> → ‘TThe index of each item in the sequence. 0 is the index of the first element in the sequence.
ofArray : ‘T array → seq<‘T>Provides a sequence view of the given array.
ofList : ‘T list → seq<‘T>Provides a sequence view of the given list.
pairwise : seq<‘T> → seq<‘T * ‘T>This function returns a sequence that contains the number of elements in the input sequence along with the number of the elements’ predecessors, except that the first element is only returned as the predecessor of the second element in the sequence.
pick : (‘T → ‘U option) → seq<‘T> → ‘UThis function is applied to successive elements, returning the first value returned by the function where a value of Some is returned by the function.
readonly : seq<‘T> → seq<‘T>In this method the sequence object is created from the given sequence object and delegated to it. The purpose of typecasting is to prevent the original sequence from being discovered and mutated again by a typecast. As an example, if you are given an array of elements to produce a sequence, you will receive the elements from the array, but you cannot cast the sequence object back to an array of elements.
reduce : (‘T → ‘T → ‘T) → seq<‘T> → ‘TEach element of the sequence is applied with a function and an accumulator argument is threaded through the calculation to bring about the output. The first two elements should be applied with the function in the beginning. You then need to feed this result along with the third element in the function and so on. The final result should be returned.
scan : (‘State → ‘T → ‘State) → ‘State → seq<‘T> → seq<‘State>This method is similar to Seq.fold, however it computes on-demand and returns the sequence of intermediate and final results as a result.
singleton : ‘T → seq<‘T>This method returns a sequence with only one item in it.
skip : int → seq<‘T> → seq<‘T>During the generation of the returned sequence, the user can specify the number of elements that should be skipped from the underlying sequence, followed by a yield of the remaining elements from the sequence.
skipWhile : (‘T → bool) → seq<‘T> → seq<‘T>If the given predicate returns true, then a sequence will be returned which skips elements of the underlying sequence in order to produce the remaining elements while the given predicate returns true.
sort : seq<‘T> → seq<‘T>By using Operators.compare, this function sorts the sequence given to it.
sortBy : (‘T → ‘Key) → seq<‘T> → seq<‘T>The given sequence is sorted using the keys provided by the projection given in the input. Using the Operators.compare method, keys are compared between each other.
sum : seq<^T> → ^TThis function sums up all elements and returns an integer.
sumByThe function gives back a sum of the results generated by applying the function to each element of a sequence in order to generate the results.
take : int → seq<‘T> → seq<‘T>The function returns the first element in a sequence up until a certain count has been specified.
takeWhile : (‘T → bool) → seq<‘T> → seq<‘T>A sequence in this function returns elements from the underlying sequence when iterated some number of times while returning true if the given predicate is true, and no further elements when the given predicate is false.
toArray : seq<‘T> → ‘T[]From a collection of items, this function creates an array.
toList : seq<‘T> → ‘T listFrom a given collection, this function will create a list.
truncate : int → seq<‘T> → seq<‘T>When enumerated, this function gives a sequence with a maximum number of elements to return when it is enumerated.
tryFind : (‘T → bool) → seq<‘T> → ‘T optionThis function returns the first element, or None if no such element exists, which is returned by the given function.
tryFindIndex : (‘T → bool) → seq<‘T> → int optionThe index is returned if the given predicate is satisfied at the onset of the sequence, otherwise if the predicate is not satisfied at onset of the sequence, then None is returned.
tryPick : (‘T → ‘U option) → seq<‘T> → ‘U optionThis function is applied to successive elements, returning the first value returned by the function where a value of some is returned by the function.
unfold : (‘State → ‘T * ‘State option) → ‘State → seq<‘T>This method returns a sequence containing the elements that would be generated by the given computation, if one is provided.
where : (‘T → bool) → seq<‘T> → seq<‘T>In order to return a new collection, you must specify a predicate that returns true for all elements found in the previous collection. Seq.filter is a synonym.
windowed : int → seq<‘T> → seq<‘T []>The function returns a sequence which produces sliding windows of elements which contain elements drawn from the sequence input. There is a fresh array returned for each window.
zip : seq<‘T1> → seq<‘T2> → seq<‘T1 * ‘T2>A list of pairs is formed by combining two sequences. If one sequence has exhausted all elements, the other sequence is ignored and no further elements are added to the other sequence. This is despite the two sequences not having equal lengths.
zip3 : seq<‘T1> → seq<‘T2> → seq<‘T3> → seq<‘T1 * ‘T2 * ‘T3>A list of triples is formed by combining three sequences. If one sequence has exhausted all elements, the other sequence is ignored and no further elements are added to the other sequence. This is despite the three sequences not having equal lengths.

Example: 

//Creating sequences let emptySeq = Seq.empty let seq1 = Seq.singleton 12printfn"The singleton sequence:" printfn "%A " seq1 printfn"The init sequence:"let seq2 = Seq.init 8 (fun n -> n * 2) Seq.iter (fun i -> printf "%d " i) seq2 printfn""//converting an array to sequence by using cast printfn"The array sequence 1:" let seq3 = [| 1 .. 8 |] :> seq<int> Seq.iter (fun i -> printf "%d " i) seq3 printfn""//converting an array to sequence by using Seq.ofArray printfn"The array sequence 2:" let seq4 = [| 1..2.. 20 |] |> Seq.ofArray Seq.iter (fun i -> printf "%d " i) seq4 printfn""
The output of the above example is as follows:
The singleton sequence:seq [12]
The init sequence:
0 2 4 6 8 10 12 14
The array sequence 1:
1 2 3 4 5 6 7 8
The array sequence 2:
1 3 5 7 9 11 13 15 17 19

The following points need to be noted:

  • The method Seq.empty creates an empty sequence when it is called.
  • In the Seq.singleton method, only one element of a sequence is created by specifying its name.
  • The method Seq.init creates a sequence of elements within which a function is passed in order to create the elements.
  • The Seq.ofArray and Seq.ofList<‘T> methods are used to create sequences from arrays and lists of elements.
  • In the Seq.iter method, you can iterate through a sequence by iterating through it.

Sum and Average Calculation

Calculating the sum and average of all elements in sequence:

Example: 

let num1 = seq { 2.0..2.0..20.0} let num2 = seq { 2..2.. 20}let sum = Seq.sum num2 let avg = Seq.average num1printfn "Sum: %d" sum printfn "Average: %f" avg

F# Sequence Seq.unfold

Using the Seq.unfold method, you can create a sequence from a state that is transformable so that each element in the sequence can be created using that state.

This is done by transforming the state before going on with the computation.

Here are the first 30 natural numbers produced by the Seq.unfold method:

Example: 

let seq1 = Seq.unfold (fun state -> if (state > 30) then None else Some(state, state + 1)) 0 printfn "The sequence of natural numbers from 0 to 30. \n" for x in seq1 do printf "%d, " x printfn" "

And the result of the above example is:

The sequence seq1 contains numbers from 0 to 30.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,

Array Conversion to Sequence and Sorting

Below is an example of creating an array and then converting it into a sequence, as well as sorting the sequence.

Example: 

let numbers = [| 5; 1; 4; 2; 3 |] let sequence = Seq.ofArray numbers let sortedNumbers = Seq.sort sequenceprintfn "Original sequence: %A" numbers printfn "Sorted sequence: %A" sortedNumbersThe output of the above example is as follows:Original sequence: [|5; 1; 4; 2; 3|] Sorted sequence: seq [1; 2; 3; 4; …]

Seq.truncate & Seq.take

This method, Seq.truncate, creates a new sequence from another sequence, but restricts the next element in the new sequence to a specified number.

With the Seq.take method, a new sequence is created which begins with precisely a specified number of elements and contains them starting from the beginning of the sequence.

Example: 

let sequence = seq { for i in 1 .. 10 -> 2*i } let truncate = Seq.truncate 5 sequence let take = Seq.take 4 sequenceprintfn"The original: " Seq.iter (fun i -> printf "%d " i) sequence printfn""printfn"The truncated: " Seq.iter (fun i -> printf "%d " i) truncate printfn""printfn"The take: " Seq.iter (fun i -> printf "%d " i) take printfn""

The output will be:

The original: 2 4 6 8 10 12 14 16 18 20
The truncated:
2 4 6 8 10
The take:
2 4 6 8

Example Explanation

Above example demonstrates the use of the Seq.truncate and Seq.take functions in F# to obtain subsequences from a sequence.

First, we define a sequence using a sequence expression that generates the values 2, 4, 6, 8, 10, 12, 14, 16, 18, 20.

Then, we use Seq.truncate to truncate the sequence to the first 5 elements and store the result in truncate. We also use Seq.take to take the first 4 elements of the sequence and store the result in.

Finally, we use Seq.iter to print out the original sequence and the two subsequences. The Seq.iter function applies a function to each element of a sequence, and in this case, we use it to print out the elements of the sequence.

Note that Seq.truncate returns a new sequence with the specified number of elements from the beginning of the original sequence. If the original sequence has fewer elements than the specified number, the entire sequence is returned.

Similarly, Seq.take returns a new sequence with the specified number of elements from the beginning of the original sequence. If the original sequence has fewer elements than the specified number, only the available elements are returned.

We value your feedback.
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0

Subscribe To Our Newsletter
Enter your email to receive a weekly round-up of our best posts. Learn more!
icon

Leave a Reply

Your email address will not be published. Required fields are marked *