F# 的特色與歷史簡介

栏目: ASP.NET · 发布时间: 6年前

内容简介: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# 有幾個特色

  1. 沒有 {} ,完全用縮排表示,類似 Python
  2. Function 傳入參數不需 () ,只需空白隔開即可
  3. 變數與 function 統一使用 let
  4. 回傳值不需要 return

這些只是語法的差異,只要習慣即可,不過平心而論,C-style 語言寫久,會發現 code 都一堆 () {}return 都是贅字,F# 這種 coding style 乾淨很多

Pipeline 與 Currying

由於 List.filterList.mapList.map 是依序處理,因此我們要不斷定義中繼變數 : oddssquares 傳入,事實上這些也是多餘的,若能省略則更好,這就是 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.mapList.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#


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Lean Analytics

Lean Analytics

Alistair Croll、Benjamin Yoskovitz / O'Reilly Media / 2013-3-18 / USD 29.99

If you're involved with a startup, analytics help you find your way to the right product and market before the money runs out. But with a flood of information available, where do you start? This book ......一起来看看 《Lean Analytics》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换