内容简介:LINQ 是 C# 3 實現 FP 重要里程碑,提供大量的 Operator,讓我們以 Pure Function 將 data 以 dataflow 方式實現。本系列將先以 Imperative 實作,然後再重構成 FP,最後再重構成 LINQ Operator,並參考 LINQ source code 的實現方式。首先從最基本的macOS High Sierra 10.13.6
LINQ 是 C# 3 實現 FP 重要里程碑,提供大量的 Operator,讓我們以 Pure Function 將 data 以 dataflow 方式實現。本系列將先以 Imperative 實作,然後再重構成 FP,最後再重構成 LINQ Operator,並參考 LINQ source code 的實現方式。
首先從最基本的 ForEach
Operator 談起。
Version
macOS High Sierra 10.13.6
.NET Core 2.1
C# 7.2
Rider 2018.1.4
User Story
在 List 中有一堆名字,想要在 console 顯示每個名字。
using System.Collections.Generic; namespace ConsoleApp { class Program { static void Main() { var names = new List<string> { "Ben", "Jafar", "Matt", "Priya", "Brian" }; } } }
Imperative : for
using System; using System.Collections.Generic; namespace ConsoleApp { class Program { static void Main(string[] args) { var names = new List<string> { "Ben", "Jafar", "Matt", "Priya", "Brian" }; for (var counter = 0; counter < names.Count; counter++) { Console.WriteLine(names[counter]); } } } }
19 行
for (var counter = 0; counter < names.Count; counter++) { Console.WriteLine(names[counter]); }
最直覺的方式 (其實應該說被制約的方式), 就是透過 List 的 Indexer 將 List 的 item 取出來,並透過 for
loop 去執行 Console.WriteLine()
。
Imperative : foreach
此時 Rider 已經提出警告,建議改用 foreach
- 按熱鍵
⌥ + ↩
,選擇Convert to foreach
using System; using System.Collections.Generic; namespace ConsoleApp { class Program { static void Main(string[] args) { var names = new List<string> { "Ben", "Jafar", "Matt", "Priya", "Brian" }; foreach (var name in names) { Console.WriteLine(name); } } } }
19 行
foreach (var name in names) { Console.WriteLine(name); }
重構成 foreach
後,就不必使用 counter
變數,也不用考慮 counter++
寫錯, for
loop 的 counter 控制,是常見的 bug 來源,所以重構成 foreach
絕對比 for
來得好。
且 foreach
的語意也比 for
清楚 ,因為不用看到 counter 實作細節。
FP : Higher Order Function
實務上這種 foreach
天天都要用到,但用 foreach
這種 statement 寫法,重複使用能力為 0,就每天都要不斷的寫 foreach
。
若我們能將 foreach
抽成 ForEach()
Higher Order Function,我們就能不斷 reuse ForEach()
,只要將不同的商業邏輯以 function 傳進 ForEach()
即可。
using System; using System.Collections.Generic; namespace ConsoleApp { static class Program { static void Main(string[] args) { var names = new List<string> { "Ben", "Jafar", "Matt", "Priya", "Brian" }; names.MyForEach(name => Console.WriteLine(name)); } private static void MyForEach<T>(this List<T> list, Action<T> action) { foreach (var iter in list) { action(iter); } } } }
22 行
private static void MyForEach<T>(this List<T> list, Action<T> action) { foreach (var iter in list) { action(iter); } }
自己以 MyForEach()
實作出 foreach
的 Higher Order Function 版本。
第 9 行
names.MyForEach(name => Console.WriteLine(name));
原來的 foreach
statement 重構成 MyForEach()
Higher Order Function 版本,只要將 Console.WriteLine()
改用 Lambda 傳入 MyForEach()
即可,如此 foreach
就能被 reuse 了。
不過 C# 在此可能感受不到 FP 所謂的 Dataflow 與 Pipeline,就語意而言,只能說我們為 List 寫了一個新 method : MyForEach()
,也就是 C# 所謂的 Extension Method,這符合傳統 C# 的 OOP 思維。
他山之石可以攻錯,我們來看看 F# 語法。
let names = ["Ben"; "Jafar"; "Matt"; "Priya"; "Brian"] let MyForEach action list = for iter in list do action iter names |> MyForEach (printfn "%A ")
names
為 List, myForEach()
為自己實作的 ForEach()
Higher Order function,若不懂 F# 語法先略過細節沒關係。
|>
為 pipline,所以可以明顯看出其語意為 names
data 以 Dataflow 與 Pipeline 方式傳給 myForEach()
執行 printf()
。
但 C# 並沒有特別提供 |>
這種 pipeline 符號,而是繼續使用 OOP 的 .
。
.
雖然方便,但心裡要知道,這裡的 .
並不是 OOP 的 method,而是 FP 的 Dataflow 與 Pipeline,只是也使用了 .
為符號,其本質是 F# 的 |>
, .
算是 |>
的 syntax sugar。
private static void MyForEach<T>(this List<T> list, Action<T> action) { foreach (var iter in list) { action(iter); } }
再舉例另外一個例子證明 .
並非 OOP 的 method,而是 FP 的 Pipeline。
我們實現 MyForEach()
時,並不是寫在 List
class 內,而是透過 static method + this 參數成為 Extenstion Method,也就是 List
data 與 MyForEach()
logic 是徹底分離,而非如 OOP 將 data 與 logic 寫在 List
class 內,也再次證明 Extension Method 是 C# 實踐 Pipeline 的手法,而 .
只是 syntax sugar,其本質是 FP 的 Pipeline。
IEnumrable
當初我們只是想為了 List 重構,所以 Extension Method 的型別只用了 List,Rider 提出建議: List<T> 其實可以重構成 IEnumerable<T>
,如此 MyForEach()
重複使用能力更高。
- 將 cursor 放在
List
上,按熱鍵⌥ + ↩
,選擇Make parameter type IEnumerable<T>
- 將
List<T>
重構成IEnumerable<T>
。
Method Group
Rider 建議將 Lambda 重構成 Method Group。
MyForEach()
後接 Lambda 天經地義,但若 function 的型別定義的很清楚,讓 compiler 可以找到正確 signature 的 Overloading method,則不用寫 Lambda,傳入 method 名稱即可,這就是 Method Group,會讓 FP 寫法更加精簡。
MyForEach()
很明確定義了 Action<T>
,就只有一個 input 參數的 function,因此 compiler 有足夠的資訊找到 Console.WriteLine()
正確的 Overloading 版本,因此適合重構成 Method Group。
- 將 cursor 放在
WriteLine
上,按熱鍵⌥ + ↩
,選擇Replace with method group
- 只要傳入 Method 名稱即可,不用寫 Lambda
Using static
其實目前的 Console.WriteLine
已經非常精簡,可讀性也高,但 C# 6 提供了 using static
,可以讓你寫出更 FP 風格的 code。
- 將 cursor 放在
Console
上,按熱鍵⌥ + ↩
,選擇Import static members
- 將
System.Console
使用using static
-
MyForEach()
只要傳入WriteLine
即可,更像是 function
LINQ : ForEach
其實 ForEach()
這種很普遍的東西,在 LINQ 早已內建,我們改用 LINQ 版本。
- 直接使用 LINQ 的
ForEach()
,將自己寫的MyForEach()
刪除
LINQ 如何實踐 ?
我們來看看 LINQ 的 source code 如何實踐 ForEach()
?
一開始對 action
進行判斷,若沒有則拋出 exception。
action
也搭配 for
進行,跟我們使用 foreach
類似。
基本上 LINQ 的 ForEach()
跟我們自己寫的 MyForEach()
差不多,但更加嚴謹
使用時機
ForEach()
表面上看起來是 foreach
statement 的 syntax sugar,但事實上 ForEach()
在 FP 的意義並非如此。
FP 將 data 以 Dataflow 與 Pipeline 方式處理,因此提供了眾多 operator,而 operator 則必須搭配 pure function,不能有 Side Effect。但 Side effect 總要有人處理, ForEach()
就是讓你統一處理 Side Effect 之處。
與 Imperative 寫法的差異是 : Imperative 總是不斷的在處理 Side Effect,因此造成結果難以預測、難以測試,bug 就是由此展開;但 FP 對於 data 處理堅持採用 Dataflow 與 Pipeline,不使用 Side Effect,因此對 data 處理是可預測且容易測試,直到最後 data 處理完,才不得已使用 ForEach()
處理 Side Effect。
如 Console.WriteLine()
就是 I/O,就是 Side Effect,這是無法避免的,最後使用 ForEach()
統一解決。
Conclusion
-
.
與 Extension Method 是 C# 的 syntax sugar,其本質就是 FP 的 data 與 logic 分離與 Pipeline - Method Group 讓 FP 寫法會更為精簡,也是 C# 很重要的發明
- FP 的 operator 主要在處理 Dataflow 與 Pipeline,應該使用 pure function,但
ForEach()
是少數讓你處理 Side Effect 的 operator,應該將 side effect 集中在ForEach()
處理
Reference
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Distributed Algorithms: An Intuitive Approach
Wan Fokkink / MIT Press / 2018-2-2 / USD 48.00
The new edition of a guide to distributed algorithms that emphasizes examples and exercises rather than the intricacies of mathematical models. This book offers students and researchers a guide to ......一起来看看 《Distributed Algorithms: An Intuitive Approach》 这本书的介绍吧!