F# For Dummys - Day 12 Collections List
Tags: fsharp
Published: 2024-05-23 08:17:06

Today we learn List, an immutable, ordered collection of elements of the same type</br>
The term "ordered" here means that the list maintains the sequence in which elements were added, doesn't mean List is sorted
#### Create List
- Explicitly specifying elements
```f#
let numbers = [1; 2; 3; 4; 5]
let names = ["Alice"; "Bob"; "Charlie"]
```
the ; could be omit between elements if you write them in seperated lines
```f#
let list123 = [ 1 // first comment
                2 // second comment
                3 // third comment
                ]
```
all elements should be the same type
```f#
let list123 = ["1"; 2; 3]
```
the first elements is string, the rest is int, will get compile error:</br>
All elements of a list must be implicitly convertible to the type of the first element, which here is 'string'. This element has type 'int'.(1,21)
- Using a range
```f#
let numbers = [1..5] // Equivalent to [1; 2; 3; 4; 5]
let evenNumbers = [2..2..10] // Equivalent to [2; 4; 6; 8; 10]
```
- Using list comprehensions
```f#
let doubles = [for x in 1..5 -> x * 2] // Equivalent to [2; 4; 6; 8; 10]
let doubles = [for x in 2..2..10 -> x * 2] // Equivalent to [4; 8; 12; 16; 20]
```
#### Get element of List
- Using index
```f#
let numbers = [1; 2; 3]
let firstNumber = numbers.[0]  // first number 1
let thirdNumber = numbers.[2]  // third number 3

printfn "firstNumber %i thirdNumber %i" firstNumber thirdNumber
```
if you use index not exist in List, will got index out of range exception
```f#
let numbers = [1; 2; 3]
let forthNumber = numbers.[4]

printfn "forthNumber %i" forthNumber
```
compile error:
Unhandled exception. System.ArgumentException: The index was outside the range of elements in the list</br>
the browser environment have bug about this exception
- Using build-in method List.item
```f#
let numbers = [1; 2; 3]
let firstNumber =  numbers |> List.item 0

printfn "firstNumber %i" firstNumber
```
- head and tail</br>
List.head, get first element
```f#
let numbers = [1; 2; 3]
let head = numbers |> List.head

printfn "head %i" head
```
List.tail, return list after removing the first element, not the last element
```f#
let numbers = [1; 2; 3]
let tail = numbers |> List.tail

printfn "tail %A" tail // tail [2; 3]
printfn "numbers %A" numbers // numbers [1; 2; 3]
```
tail is a new List not including the first element, it won't change the value of original List numbers
#### Loop a List
- for...in
```f#
let numbers = [1; 2; 3]

for number in numbers do
    printfn "Number: %d" number
```
- List.iter
```f#
let numbers = [1; 2; 3]

List.iter (fun number -> printfn "Number: %d" number) numbers
```
fun define a lambda function here, it's equivalent to
```f#
let numbers = [1; 2; 3]
let printElement number = 
    printfn "Number: %d" number

List.iter printElement numbers
```
#### Pattern Matching with List
```f#
let numbers0 = []
let numbers1 = [1]
let numbers3 = [1; 2; 3]
let describeList lst =
    match lst with
    | [] -> "The list is empty"
    | [x] -> sprintf "The list has one element: %d" x
    | [x; y] -> sprintf "The list has two elements: %d and %d" x y
    | x :: xs -> sprintf "The list starts with %d and has more elements" x

printfn "%s" (describeList numbers0)  // The list is empty
printfn "%s" (describeList numbers1)  // The list has one element: 1
printfn "%s" (describeList numbers3)  // The list starts with %d and has more elements
```
#### Operate List
lists are indeed immutable, which means that you cannot modify a list after it has been created. Any operation that appears to modify a list (such as appending or prepending an element) actually creates a new list
- Prepending an element
```f#
let originalList = [2; 3; 4]
let newList = 1 :: originalList

printfn "Original List: %A" originalList // Original List: [2; 3; 4]
printfn "New List: %A" newList //  New List: [1; 2; 3; 4]
```
- Appending an element
```f#
let originalList = [1; 2; 3]
let newList = originalList @ [4]

printfn "Original List: %A" originalList  // Original List: [1; 2; 3]
printfn "New List: %A" newList //  New List: [1; 2; 3; 4]
```
- Concatenate Lists
```f#
let originalList = [1; 2; 3]
let newList = List.append originalList [4]

printfn "Original List: %A" originalList
printfn "New List: %A" newList
```
- Filter</br>

you can not remove an element in List directly as List is immutable, but you can filter element based on some condition, put them into a new List
```f#
let numbers = [1; 2; 3; 4; 5]
let evenNumbers = List.filter (fun x -> x % 2 = 0) numbers

printfn "evenNumbers: %A" evenNumbers // evenNumbers: [2; 4]
```
- Map</br>

apply an operation to every elements in List, you can use loop/List comprehensions to achieve that, or use Map
```f#
let numbers = [1; 2; 3; 4; 5]
let doubleNumbers = List.map (fun x -> 2 * x) numbers

printfn "doubleNumbers: %A" doubleNumbers // doubleNumbers:  [2; 4; 6; 8; 10]
```
- Fold</br>

"fold" or "compress" elements of List of into a single value by applying a function repeatedly
```f#
let numbers = [1; 2; 3; 4; 5]
let sum = List.fold (fun acc x -> acc + x) 0 numbers  // 15

printfn "sum: %i" sum // sum: 15
```
step 1: `acc = 0` and `x = 1` New `acc = 0 + 1 = 1`</br>
step 2: `acc = 1` and `x = 2` New `acc = 1 + 2 = 3`</br>
step 3: `acc = 3` and `x = 3` New `acc = 3 + 3 = 6`</br>
step 4: `acc = 6` and `x = 4` New `acc = 6 + 4 = 10`</br>
step 5: `acc = 10` and `x = 5` New `acc = 10 + 5 = 15`</br>