函數(shù)式編程

函數(shù)式編程

1.什么是函數(shù)式編程?

functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data

來自維基百科的定義,解釋道:
函數(shù)式編程是一種編程范式用以構建計算機程序結構和元素,用數(shù)學函數(shù)的標準來評估程序的運算,避免改變狀態(tài)和可變數(shù)據(jù)。

從定義我們可以get到兩個點:

  1. 數(shù)學函數(shù)的標準是什么標準?
  2. 為什么要避免狀態(tài)和可變數(shù)據(jù)?

1.1 數(shù)學函數(shù)的標準

在數(shù)學中,函數(shù)為兩集合間的一種對應關系:輸入值集合中的每項元素皆能對應唯一一項輸出值集合中的元素。例如實數(shù)x對應到其平方x*x的關系就是一個函數(shù),若以3作為此函數(shù)的輸入值,所得的輸出值便是9。

這一定義的關鍵詞為【唯一】,表示著如果我有一個函數(shù)y=f(x),對于唯一確定的x,我們必定能得到唯一的y。
這一定義又與我們react中常聽到的"Pure functions"(純函數(shù))類似,我們用來判定一個函數(shù)是否為純函數(shù):

  • 如果給出相同的參數(shù),它返回相同的結果
  • 它不會引起任何可觀察到的副作用

Tips: 函數(shù)副作用指當調(diào)用函數(shù)時,除了返回函數(shù)值之外,還對主調(diào)用函數(shù)產(chǎn)生附加的影響。例如修改全局變量(函數(shù)外的變量)或修改參數(shù)

1.2 為什么要避免狀態(tài)和可變數(shù)據(jù)?

首先,我們來看看一個函數(shù)引起狀態(tài)和數(shù)據(jù)改變的情況。

function fn() {
    let num = 100;
    return function () {
        return num - 1;
    }
}
f = fn();
f();
f();

上面這段程序大家都很熟悉,典型的閉包,每次調(diào)用f(),函數(shù)都會在上一次調(diào)用的基礎上執(zhí)行減1操作,很多時候編程中我們需要使用到這樣的技巧。

這就是一個典型的函數(shù)引起數(shù)據(jù)改變的例子。乍一看沒啥問題,那么如果我們是處于一個并發(fā)操作高的環(huán)境呢。

如果有100個人,同時請求執(zhí)行這段代碼,我們能預想到實際執(zhí)行返回結果嗎?

很顯然,不能,在服務端經(jīng)常會遇到這種情況,我們常見的解決方案就是【加鎖】,一次僅執(zhí)行一次,那么執(zhí)行結果就變得可控了。

但是,函數(shù)式編程確能很好的解決這個問題,函數(shù)式編程的主要思想就是,對于一個確定的操作,我返回唯一的結果,從而保證有跡可循。

通常,我們改變數(shù)據(jù)和狀態(tài)的方式是通過賦值。那么可想而知,我們可以大膽地推測函數(shù)式編程的核心規(guī)則是【避免賦值】。在另一方面,Immutable.js保證數(shù)據(jù)一旦創(chuàng)建,就不能再被更改,也避免了狀態(tài)和數(shù)據(jù)的改變。

2.正宗的函數(shù)式編程語言——Lisp

Lisp是正宗的函數(shù)式編程語言,了解函數(shù)式我們不得不究其根源了解一下Lisp。

2.1 Lisp的基本語法

2.1.0 Lisp環(huán)境

首先,您需要獲得一個LISP解釋器(GNU Common Lisp)

Windows 用戶安裝完了之后,您只需從開始菜單中找到它并單擊以運行就可以了。

Linux 用戶安裝完之后,在終端運行 gcl 即可啟動LISP編譯環(huán)境。
下文中的 > 號表示開始輸入指令

2.1.1 表達式(算數(shù)表達式)
> (+ 1 2) // 3
> (+ 1 2 3 4) // 10
> (+ (*3 3) (* 4 4) ) // 25

復雜一點的表達式

(+ (*3 (+ (* 2 4) (+3 5) ) ) (+ (-10 7) 6 ) ) // ???

Lisp的求值規(guī)則:

  1. 求各子表達式的值(右值)
  2. 將所有右值應用到運算符上就得最終結果
2.1.2 命名(定義)
> (define size 5)
> (* size 2) // 10
> (define (add a b) (+ a b))
> (add 1 2) // 3
2.1.3 if
> (define (abs x)
    (if (< x 0)
        (- x)
        x
     )
  )
2.1.4 遞歸&迭代(以階乘為例)

遞歸

(define (factorial n)
    (if (=n 1)
        1
        (* n (factorial (- n 1))
    )
)

迭代

> (define (factorial n)
    (fact-iter 1 1 n)
) 

> (define (fact-iter result n n-max)
    (if (> n max-n)
        result
        (fact-iter 
            (* n result)
            (+ n 1)
            (max -n)
        )
)
  • 遞歸: 先遞進(展開),再回歸(求值)
  • 迭代: 從一個狀態(tài)到下一個狀態(tài)(有多個變量表示狀態(tài),每次更新這幾個變量)
2.1.5 more

更多精彩,盡在無盡的探索中ANSI Common Lisp 中文翻譯版

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容