F# For Dummys - Day 16 Collections Sequence
Tags: fsharp
Published: 2024-05-28 11:01:54

Today we learn Sequence, represents an ordered, read-only series of elements
#### Why Sequence
-   Sequences are particularly useful when you have a large, ordered collection of data but do not necessarily expect to use all of the elements.
-   Individual sequence elements are computed on-demand, so a sequence can provide better performance than a list in situations in which not all the elements are used</br>

**What is computed on-demand**</br>
like buying a burger in KFC, the staff prepared 1000 burgers in the early morning, so you got your burger immediately, all the burgers are made before customer order, this is called *eager evaluation*</br>

then you ordered a fried chicken, the staff replies: sorry, you have to wait for a minute, as we need to fry the chicken first, they only do the work when someone ordered, this is called *lazy evaluation*, also known as *On-demand computation*

#### Create Sequence
- Explicitly specifying elements
```f#
let seq1 = seq [1; 2; 3; 4]
printfn "seq: %A" seq1
```
- Using range expression
```f#
let seq1 = seq { 1 .. 10 } // from 1 to 10
printfn "seq: %A" seq1 // seq: seq [1; 2; 3; 4; ...]
```
use step in range expression
```f#
let seq1 = seq { 1 .. 2 .. 10 } // start from 1, add 2 each time
printfn "seq: %A" seq1 // seq: seq [1; 3; 5; 7; ...]
```
- Using for loop
```f#
let seq1 = seq { for i in 1 .. 10 -> i }
printfn "seq: %A" seq1
```
- ofList</br>
Views the given list as a sequence
```f#
let inputs = [ 1; 2; 5 ]
let seq1 = inputs |> Seq.ofList
printfn "seq: %A" seq1
```
- ofArray</br>
```f#
let inputs = [| 1; 2; 5 |]
let seq1 = inputs |> Seq.ofArray
printfn "seq: %A" seq1
```
- Seq.initInfinite
generate an infinite sequence
```f#
let infiniteSeq = Seq.initInfinite (fun i -> i * 2)
printfn "infiniteSeq %A" infiniteSeq
```
define a start for infinite sequence
```f#
let start = 5
let infiniteSeq = Seq.initInfinite (fun i -> i + start)
printfn "infiniteSeq %A" infiniteSeq // infiniteSeq seq [5; 6; 7; 8; ...]
```
or define a start like this
```f#
let infiniteSeq = Seq.initInfinite ((+) 5)
printfn "infiniteSeq %A" infiniteSeq // infiniteSeq seq [5; 6; 7; 8; ...]
```
#### Loop Sequence
- for loop
```f#
let numbers = seq { 1 .. 10 }

for number in numbers do
    printfn "Number: %d" number
```
- Seq.iter
```f#
let numbers = seq { 1 .. 10 }

Seq.iter (fun x -> printfn "Number: %d" x) numbers
```
#### Access element
- Seq.item

syntax: Seq.item index source, thrown ArgumentException when the index is negative or the input sequence does not contain enough elements
```f#
let numbers = seq { 1 .. 10 }
let thirdElement = Seq.item 2 numbers  // Indexing is zero-based

printfn "The third element is %d" thirdElement
```
- Seq.head && Seq.tail

Seq.head: Returns the first element of the sequence</br>
Seq.tail: Returns a sequence that skips 1 element of the underlying sequence and then yields the remaining elements of the sequence
```f#
let numbers = seq { 1 .. 10 }
let firstElement = Seq.head numbers
let restElement = Seq.tail numbers

printfn "The first element is %d" firstElement // The first element is 1
printfn "The rest element is %A" restElement // The rest element is seq [2; 3; 4; 5; ...]
```
#### Operate element
- Seq.updateAt

Return a new sequence with the item at a given index set to the new value</br>
syntax: Seq.updateAt index value source
```f#
let newSeq = Seq.updateAt 1 9 seq { 0; 1; 2 }
// let newSeq = Seq.updateAt 1 9 (seq { 0; 1; 2 })
printfn "newSeq: %A" newSeq // newSeq: seq [0; 9; 2]
```
- Seq.filter

Returns a new collection containing only the elements of the collection for which the given predicate returns "true". This is a synonym for Seq.where</br>
syntax: Seq.filter predicateFunc sourceSeq
```f#
let numbers = seq { 1 .. 10 }
let evenNumbers = Seq.filter (fun x -> x % 2 = 0) numbers

Seq.iter (fun x -> printfn "Even Number: %d" x) evenNumbers
```
- Seq.map

Builds a new collection whose elements are the results of applying the given function to each of the elements of the collection</br>
syntax: Seq.filter mappingFunc sourceSeq
```f#
let numbers = seq { 1 .. 10 }
let doubleNumbers = Seq.map (fun x -> x * 2) numbers

Seq.iter (fun x -> printfn "doubleNumbers: %d" x) doubleNumbers
```
- Seq.fold

Applies a function to each element of the collection
syntax: Seq.fold folderFunc state sourceSeq
```f#
type Charge =
     | In of int
     | Out of int
let inputs = [In 1; Out 2; In 3]
let balance = Seq.fold (fun acc charge ->
    match charge with
    | In i -> acc + i
    | Out o -> acc - o) 0 inputs

printfn "balance %A" balance // balance 2
```
or use pipeline operator to pass state sourceSeq, here is ||> for passing more than one param
```f#
type Charge =
     | In of int
     | Out of int
let inputs = [In 1; Out 2; In 3]
let balance = (0, inputs) ||> Seq.fold (fun acc charge ->
    match charge with
    | In i -> acc + i
    | Out o -> acc - o)

printfn "balance %A" balance // balance 2
```