内容简介:F# 並不是一個新語言,早在 2005 年就已經發行 1.0,隨著 .NET Core 的跨平台,也將 F# 帶進了 .NET Core,既然在 .NET Core 我們已經有了 C#,為什麼要關注 F# 呢 ?macOS High Sierra 10.13.3.NET Core 2.1.4
F# 並不是一個新語言,早在 2005 年就已經發行 1.0,隨著 .NET Core 的跨平台,也將 F# 帶進了 .NET Core,既然在 .NET Core 我們已經有了 C#,為什麼要關注 F# 呢 ?
Version
macOS High Sierra 10.13.3
.NET Core 2.1.4
F# 4.1
FSharp 的歷史
F# 並不像 C-style 語言,反而比較像 Python,事實上 F# 是從 ML、OCaml、 Python 、Haskell、Scala、Erlang … 等語言獲得靈感,除了 Python 大家較為熟悉外,剩下的都是 Functional Programming Language,由此可見 F# 的 F 就是 F
untional,所以 F# 號稱是 function first language,也就是 F# 雖然也支援 OOP,但 FP 是其主要特色。
個人大概在 2010 年曾經接觸過 F#,當初的感覺 F# 是個 外星語言
,很難體會 F# 的優點在哪裡,學沒多久就放棄了,但經過這幾年 Laravel Collection、JavaScript 、Linq 、Rx.js 與 AWS Lambda 的轟炸,越來越覺得 FP 的可愛,OOP 也能藉由 FP 手法,產生出現更優雅的實作方式。
這幾年一直想尋找一個 FP 語言來練習,期間摸過 Elixir 、Scala、Clojure,但成效一直有限,一直到最近複習 F#,才發現 F# 是一個很簡單的 FP 語言,以前看不懂的地方,瞬間都看懂了,只是當年還無法欣賞 F#。
Functional Programming 定義
一個語言要能實現 FP,必須有 4 個條件:
- 能將 function 指定為變數
- 能將 function 存到 collection 內
- 能將 function 以參數型式傳入 function
- 能在 function 回傳 function
簡單來說,function 要能如一般變數與 object 一樣使用。
能將 function 以參數型式傳入 function
導致了 Higher Order Function 的觀念出現,如 Rx.js 一堆 operator,就是 Higher Order Function。
能在 function 回傳 function
則導致了 Pipeline、Compose 與 Currying 的觀念出現。
一般 OOP 語言都會某種程度的支援 FP,如 Higher Order Function 在 C#、JavaScript、PHP … 都可以實現。
但 Pipeline、Compose 與 Currying 在一般 OOP 語言則沒有,或者要另外安裝其他 package 才能實現,但這些觀念在 F# 都是原生支援。
User Story
我們想將陣列 1, 2, 3, 4, 5
的資料中,將所 奇數
平方再加 1
。
Task
根據 JavaScript、Linq 、Rx.js 的經驗,我們不再使用迴圈,而會使用 Higher Order Function 來解決問題。
Higher Order Function
Program.fs
open System
[<EntryPoint>]
let main argv =
let numbers = [1; 2; 3; 4; 5]
let isOdd x = x % 2 <> 0
let square x = x * x
let addOne x = x + 1
let func values =
let odds = List.filter isOdd values
let squares = List.map square odds
let result = List.map addOne squares
result
printfn "%A" (func numbers)
0
12 行
let odds = List.filter isOdd values
使用 List.filter
先找出所有 奇數
。
-
isOdd: 傳入判斷奇數的 function -
values: 傳入欲處理資料 -
odds: 回傳所有奇數
第 7 行
let isOdd x = x % 2 <> 0
定義 isOdd
,當 % 2
餘數不為 0
時為 奇數
。
在 F#,因為已經將 function 視為一般變數,所以無論是 value 或 function,都統一使用 let
13 行
let squares = List.map square odds
既然已經找出所有 奇數
,接下來就是使用 List.map
計算 平方
。
-
square: 傳入計算平方的 function -
odds: 傳入所有奇數 -
squares: 回傳所有平方
第 8 行
let square x = x * x
定義 square
,計算平方。
14 行
let result = List.map addOne squares
既然已經計算出所有 奇數的平方
,接下來就是使用 List.map
計算 +1
。
-
square: 傳入計算+1的 function -
odds: 傳入所有奇數的平方 -
squares: 回傳所有平方+1
第 9 行
let addOne x = x + 1
定義 addOne
,計算 +1
。
15 行
result
要回傳的變數, F# 不用寫 return
。
相對於 C-style 語言,我們發現 F# 有幾個特色
-
沒有
{},完全用縮排表示,類似 Python -
Function 傳入參數不需
(),只需空白隔開即可 -
變數與 function 統一使用
let -
回傳值不需要
return
這些只是語法的差異,只要習慣即可,不過平心而論,C-style 語言寫久,會發現 code 都一堆 ()
{}
與 return
都是贅字,F# 這種 coding style 乾淨很多
Pipeline 與 Currying
由於 List.filter
、 List.map
與 List.map
是依序處理,因此我們要不斷定義中繼變數 : odds
與 squares
傳入,事實上這些也是多餘的,若能省略則更好,這就是 Pipeline。
Program.fs
open System
[<EntryPoint>]
let main argv =
let numbers = [1; 2; 3; 4; 5]
let isOdd x = x % 2 <> 0
let square x = x * x
let addOne x = x + 1
let func values =
values
|> List.filter isOdd
|> List.map square
|> List.map addOne
printfn "%A" (func numbers)
0
12 行
values |> List.filter isOdd |> List.map square |> List.map addOne
|>
為 F# 的 Pipeline 符號,表示將 function 的 output 作為下一個 function 的 input。
因此我們可以透過 |>
表示先執行 List.flter
,然後再將結果傳入 List.map
,最後再將結果傳入 List.map
,這樣可以很清楚的表示流程,語意比 imperative 寫法更清楚。
Q : 可以明明 List.map
與 List.filter
是 2 個參數,第 1 個參數是 function,第 2 個參數是 value,但為什麼 value 都不用傳呢 ?
當 function 參數沒有傳完全時,F# 將回傳一個新的 function,新的 function 只要傳入剩下的參數即可,這稱為 Currying。
當 List.filter isOdd
只傳入 1 個參數時,由於參數沒有傳完整,將回傳一個新的 function,然後 |>
再將 values
傳入新的 function,如此 List.filter
才算完整,才能回傳 所有奇數
,最後再將 所有奇數
透過 |>
傳給下一個 List.map
,剩下以此類推。
Compose
Pipeline 雖然已經夠清楚,但 pipeline 基本上仍然是回傳 value,若我們能將所有 function 先組合好,最後統一透過一個 function 執行,那就更好了,這就是 Compose。
Program.fs
open System
[<EntryPoint>]
let main argv =
let numbers = [1; 2; 3; 4; 5]
let isOdd x = x % 2 <> 0
let square x = x * x
let addOne x = x + 1
let sqaureAddOne = square >> addOne
let func = List.filter isOdd >> List.map sqaureAddOne
printfn "%A" (func numbers)
0
12 行
let func = List.filter isOdd >> List.map sqaureAddOne
>>
為 F# 的 compose 符號,專門負責組合 function。
因為連續兩個 List.map
,因此我們先將 square 與 addOne 組合成新的 sqaureAddOne
function,再交給 List.map
執行。
由於是先執行 List.fiter
,再執行 List.map
,因此使用 List.filter >> List.map
。
>>
不只代表 compose,也代表執行方向,所以也有 <<
。
Conclusion
- 本文簡單的展示 F# 最關鍵的 Pipeline、Currying 與 Compose,這些都是 OOP 語言很難見到的強悍功能,透過 F# 的簡單實作,讓我們在練習 FP 時更加方便
-
F# 並不是要取代 C#,事實上在 .NET Core,C# 仍是必學的語言,只是透過學習 F#,能訓練自己 FP 的思維,進而用在 C# 與 TypeScript 上
-
若語言間的 paradigm 相同,只是 syntax 不同,則沒有學習新語言的必要;但若透過更好的 syntax,讓你學到不同的 paradigm,這就有意義了,這就是學習 F# 的原因
Sample Code
完整的範例可以在我的 GitHub 上找到
Reference
F# , Tour of F#
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- WWDC2017四大系统更新汇总:惊喜多,还有中国特色
- MSF5一些特色以及如何使用其免杀
- 面向NLP场景应用的智能辅助建模(五)特色与优势
- 苹果或推出中国特色版iPhone 去Face ID配屏幕指纹
- 基于 Debian 以 Plasma 为特色的发行版 Neptune 发布 5.4 版
- Percona Server for MongoDB 4.0.10-5 发布,以 Hashicor Vault 集成为特色
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
HTTP Essentials
Stephen A. Thomas、Stephen Thomas / Wiley / 2001-03-08 / USD 34.99
The first complete reference guide to the essential Web protocol As applications and services converge and Web technologies not only assume HTTP but require developers to manipulate it, it is be......一起来看看 《HTTP Essentials》 这本书的介绍吧!