深入探討 F# 之 List
F# 除了使用 .NET Framework 所提供的型別外,自己還定義了不少適合 FP 的型別,其中最常用的就是 List
,但這與 C# 的 List<T>
不同,反而類似 C# 的 ImmutableList<T>
。
Version
macOS High Sierra 10.13.3
.NET Core SDK 2.1.101
Rider 2017.3.1
F# 4.1
Definition
List
將相同型別的 value 整合成單一 value,且每個 element 必須為 immutable
簡單來說,F# 的 List 就是 linked list 資料結構,為了實現 FP 的 Immutability,每個 element 建立後就不可再修改。
建立 List
List Literal
1 | let list1 = [ 1; 2; 3 ] |
List 使用 []
中括號表示,element 之間以 ;
隔開。
- List 天生就是泛型,不過並不需要如 C# 宣告泛型型別
List<int>
,Type Inference 會自動推導出int list
。
F# 的型別都是寫在後面,不過 List 例外,element 型別寫在 list 前面
1 | let list1 = [ |
List 也可以使用換行的方式,此時可以不用加 ;
隔開,適用於 element 量較多時,或需要在 element 加上註解時。
1 | let list1 : Control list = [ new Button(); new CheckBox() ] |
List 的 element 必須相同型別,若 element 是物件時,則有例外。基於 里氏替換原則
,若 element 的型別為 父類別
或 interface
時,則 element 允許基於相同 父類別
或 interface
的物件。
1 | let list1 = [] |
空 list 則以 []
表示。
List Range
1 | let list1 = [ 1 .. 10 ] |
若 list 的資料有規律性,則不必全部列出,可使用 ..
,只要列出頭跟尾即可。
1 | let list1 = [ 1 .. 2 .. 10 ] |
若 list 資料為 等差級數
,則可在 ..
與 ..
之間加上 step value,其中 step value 也可以是 負數
。
List Comprehension
若資料的變化性更大,已經無法單純使用 List Range 描述,則可使用更進階的 List Comprehension 表示。
1 | let list1 = [ |
直接將 for ... in … do
寫在 []
之內,yield
的 expression 即為 element。
1 | let list1 = [for i in 1..10 -> i * i] |
亦可將 do yield
省略,直接將 for ... in ... ->
寫在 []
內,將 expression 寫在 ->
之後。
對於簡單的 expression,建議使用
for ... in ... ->
語法較精簡,若還需更複雜的描述,則必須使用for ... in ... do yield
List.init
根據 index 值建立新的 list
1 | let list1 = List.init 10 (fun idx -> idx * idx) |
List Operator
:: Operator
1 | let list1 = [ 1; 2; 3 ] |
將 value 加到 :
之前, 將加到 list 最前面。
@ Operator
1 | let list1 = [ 1; 2; 3 ] |
@
將兩個 list 合併為一個 list。
Property
List 本身提供了不少 property 可用。
Head
獲得 List 第 1 個 element。
'T
1 | let list1 = [ 1 .. 3 ] |
Tail
獲得第 1 個 element 除外,剩下的 list
'T list
1 | let list1 = [ 1 .. 3 ] |
就算 Tail 只剩下 1 個 element,仍然是 1 個 element 的 list。
IsEmpty
判斷 list 是否為空
bool
1 | let list1 = [ 1 .. 2 ] |
Item
傳回指定 index 的 element 值
'T
1 | let list1 = [ 1 .. 3 ] |
List 的 index 為 zero-based。
Length
獲得 list 的 element 個數
int
1 | let list1 = [ 1 .. 3 ] |
Static Method
List.Cons
將 value 加到 list 最前面,產生新的 list,相當於
::
operator
head: 'T * tail: 'T list -> 'T list
1 | let list1 = [ 1 .. 3 ] |
List.Cons()
傳入為一 tuple,第 1 個 element 為 value,第 2 個 element 為 list。
List.Empty
根據 list type 回傳一個 empty list
'T list
1 | type myList = int list |
Q : List.Empty 與 List.empty 有何差異?
1 | List.empty<int> |
相同
- 均回傳 empty list
相異
List.Empty
為List
type 的 static method;List.empty()
定義在 List moduleList.Empty
須先定義 type;List.empty()
可由泛型直接指定 element type
Recursive Function
1 | let rec sum list = |
List 在配合 recursive function 時,在 Pattern Matching 常搭配 head :: tail
,直接將 list 拆成兩半,然後將 tail
傳入 recursive function 繼續運算。
Conclusion
- List 為 F# 最常用的 collection,概念雖然不難,但有一些 F# 獨特的語法,還是得花一點時間熟悉
Reference
Microsoft Docs, Lists
Wikibooks, F Sharp Programming/Lists
Chris Smith, Mastering F# Lists
Chris Smith, Pramming F# 3.0 2nd