F# 的特色與歷史簡介

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

内容简介: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#


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

查看所有标签

猜你喜欢:

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

HTTP Essentials

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》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具