深入探討 F# 之 Tuple
Tuple
是 FP 語言常見的型別,且 C# 7.0 也開始有 Tuple
,讓我們藉由 F# 學習該如何活用 Tuple
。
Version
macOS High Sierra 10.13.3
.NET Core SDK 2.1.101
Rider 2017.3.1
F# 4.1
Definition
Tuple
將不同型別的 value 整合成單一 value
在將不同型別整合成單一型別,F# 提供了 3 種方式:
Tuple
Record
Union
其中 Tuple
與 Record
提供類似 AND 的方式將不同型別加以整合,而 Union
則提供類似 OR 的方式將不同型別加以整合。1 1關於 F# 的 Union,詳細請參考 深入探討 F# 之 Discriminated Union
Record
與 Tuple
的差異在於 Record
為 named type,須事先定義型別,而 Tuple
則為 unmamed type,可直接使用,Type Inference 會幫我們推導出 Tuple
型別。
建立 Tuple
相同型別
1 | let dinner = ("green eggs", "ham") |
將 green eggs
與 ham
兩個 string 整合成一個 dinner
tuple。
Type Inference 自動推導出 dinner
tuple 為 string * string
。
Tuple
型別表示法,是以 *
串接各型別,因為 Tuple
概念是 AND,所以使用 *
。
不同型別
1 | let dinner = ("green eggs", 16); |
若只是將 tuple 用在相同型別,則顯示不出 tuple 的威力,相同型別大可使用 Array
或 List
,tuple 讓我們可以將 不同型別
包在同一個 tuple。
Type Inference 自動推導出 dinner
tuple 為 string * int
。
1 | let nested = (1, (2.0, 3M), (4L, "5", '6')) |
甚至也可以巢狀的方式使用 tuple。
分解 Tuple
Tuple 將不同 value 整合單一 tuple 後,馬上會遇到另外一個問題:
Q:如何將單一 tuple 分解成 value?
F# 提供 3 種方式:
fst()
與snd()
- Let Binding
- Pattern Matching
fst() 與 snd()
1 | let students = ("John", "Smith") |
若只是兩個 element 的 tuple,可使用 fst()
與 snd()
function 分解 tuple。
沒有
trd()
,但可自行建立let trd (_, _, z) = z
Let Binding
1 | let students = ("John", "Smith", "Jessie") |
直接使用 let
,會自動將 tuple 分解到各別 value,也沒 element 數限制。
1 | let students = ("John", "Smith", "Jessie") |
若只需部分 element,可使用 _
忽略之。
1 | let students = ("John", "Smith", "Jessie") |
當 tuple 傳進 function 參數時,可直接分解成 value。
Pattern Matching
1 | let students = ("John", "Smith", "Jessie") |
亦可使用 Pattern Matching 對 tuple 加以分解。
活用 Tuple
將 Tuple 當成參數
1 | let values = (2, 3, 4) |
values
為 tuple,而 add()
為 int * int * int -> int
。
當 values
傳進 add()
時,會自動分解成 x
、 y
與 z
。
Tuple vs. Partial Function Application
1 | let add1 x y z = x + y + z |
雖然都是 x + y + z
,但 add1()
與 add2()
意義完全不同。
add1()
為 int -> int -> int -> int
。
add2()
為 int * int * int -> int
。
add1()
可使用 Partial Function Application 方式使用,也就是若只傳一個參數 int
,將回傳 int -> int -> int
function,若傳入兩個參數 int
,將回傳 init -> int
function。
add2()
則必須一次傳入整個 tuple,不能只傳 部分參數
。
Q:我該使用 Tuple 或 Partial Function Application 呢?
- 完全視
設計考量
與語意
- Partial Function Application 使用上會比 Tuple 靈活
- 若你不希望被當成 Partial Function Application 使用,則使用 Tuple。
將 Tuple 當成回傳值
1 | let divRem a b = |
有些 function 會希望一次回傳多個值,如 兩數相除
,會希望同時傳回 商數
與 餘數
。
傳統程式語言我們會回傳 Array
或 List
,但有相同型別的限制;在 OOP 我們會回傳 Object
,但必須事先建立 class;但在 FP 我們可回傳 Tuple
。
Conclusion
- Tuple 不用事先定義型別,可以直接使用,特別適合 function 只使用一次的
參數
或回傳值
型別 - 在 OOP,若我們想將
多個參數
合併成單一參數
,或將多回傳值
合併成單一回傳值
,我們必須使用Object
,但常常糾結是否該特別開一個 class 型別;但在 FP,我們可以隨時使用 tuple,也能享受 compile 強型別的檢查
Reference
Microsoft Docs, Tuples
F# for fun and profit, Tuples
Chris Smith, Pramming F# 3.0 2nd