理解函數式編程的本質

functional programming

前言

筆者在以前曾經花時間學習了Haskell(所謂的純函數式編程語言),而且在也研究過一段時間的函數式響應編程,也就是Functional reactive programing,整體有直觀的感受,但最近在學習kotlin的過程當中,忽然對於函數式編程又引發了新的思考,因而花時間去研究了一番,究竟什麼是函數式編程,總結成文但願可以幫助你理解。react

一些理解存在誤差

函數式編程在各個領域常常被說起,但不少文章去講函數式編程都會有些誤差,並無抓到本質。或者說講了函數編程常見的寫法,但沒有講清楚究竟什麼是函數式編程。只有理解了這個本質問題,才能在後續的學習中一路順風。express

(爲了理解函數式編程,本文後面一些名詞直接採用英文名詞進行解釋說明,由於生硬的翻譯過來,會加大理解的難度)編程

什麼是函數(Function)

這個問題雖然看上去很簡單,但其實並不簡單。bash

函數(function)這個名詞來自於數學,函數經過一個給定的值,計算出另一個值,也就是上學時常見的f(x)。編程語言

在一般的理解中,下面代碼裏面,f1,f2,f3一般都被叫作函數:ide

//僞代碼 函數無入參,返回2
def f1(): return 2
//函數有參數x,返回 x+1
def f2(int x): return x+1
//函數無入參,無返回值,打印hello world
def f3(): print("hello world")
複製代碼

但實際上,函數(function)和procedure是有區別的: function 經過運算返回一個值,而procedure只執行一段代碼,沒有返回值。 這一點對於後面的理解是很是有幫助的,首先要區分出兩者。函數式編程

再回到上面的代碼中,f1,f2,爲function而f3爲procedure。函數

什麼是Pure Function

Pure:純的; 單純的; 純真的; 乾淨的 咱們將知足下面兩個條件的函數稱做Pure Function:學習

  1. 函數不會產生side effect(no side effect)
  2. 函數知足referential transparency這個條件 (原諒我不會翻譯這兩個名詞)

Side effect

函數調用後不會對外部狀態產生影響,好比下面這段代碼中sum函數是no side effect的:lua

def sum(a,b): return a+b
複製代碼

產生side effect的函數長成什麼樣呢?其實常常會寫這樣的函數:

int sum = 0
def plus(a,b){
  sum = a + b
  return sum
}
複製代碼

plus函數除了計算參數之和之外,還改變了外部變量sum的值,咱們plus這個函數產生了side effect。

常見的Side effect

  • 改變外部變量的值(上面的例子中plus函數)
  • 像磁盤中寫入數據
  • 將頁面上的一個按鈕設置爲可點擊,或者不可點擊

前面提到function和procedure的不一樣點,在side effect這個角度來說,pure funcion不會產生side effect,procedure一般會產生side effect。

Referential transparency

Referential transparency means that given a function and an input value, you will always receive the same output. That is to say there is no external state used in the function.

Referential transparency means that you can replace anyexpression in the program with the result of evaluating that expression (or vice versa) without changing the meaning of the program.

  • 知足Referential Transparency的函數能夠將能夠將用函數計算的結果替換表達式自己,而不影響程序的邏輯。
  • 給定指定的參數,在任什麼時候候返回的值都是相同的。不受其餘外部條件影響。

二者說的意思是同樣的,只是表達的角度是不一樣的

舉個知足RT的例子

def f(): return 2

print(f() + f())
print(2)
複製代碼

下面這段代碼中的f()是知足RT的函數,按照上面的解釋,咱們能夠將f()的結果也就是2替換掉f(),不會影響程序自己的邏輯:

def f(): return 2

print(2 + f())
print(2)
複製代碼

或者這樣替換:

def f(): return 2

print(f() + 2)
print(2)
複製代碼

從另外一個角度說,f()這個函數不管在何時調用,返回的值都是同樣的,不會發生改變(沒有外部條件影響)

舉個不知足RT的例子

int counter = 0

def f(x){
  counter += 1
  return x + counter
}
複製代碼

這個例子中,f(x)這個函數不知足RT 下面的代碼中,當咱們用f(1)的計算結果一次替換代碼中f(1)自己時,程序的邏輯是錯誤的:

//原始的代碼執行結果是:3
f(1) + f(1)

//把f(1)的結果1替換進來,下面函數執行的結果是:2
f(1) + 1

//一樣,獲得2
1 + f(1)

//獲得2
1 + 1
複製代碼

咱們不能用執行的結果替換函數自己,

換個角度,下面兩行代碼執行的結果也不一樣

f(1) + f(1)
2 * f(1)
複製代碼

雖然入參都爲1,但f(1)在不一樣時候調用獲得的結果不一樣,所以f不知足RT這個條件

回到pure function

理解了side effect 和 referential transparency的含義,咱們再來重溫pure function的定義,就很好理解了:

  • No side effect
  • Referential transparency

知足這兩個條件的函數,稱之爲pure function

什麼是函數式編程

理解了pure function以後,咱們回到什麼是functional programing,就會有種豁然開朗的感受:

Functional programming is about writing pure functions

函數式編程最大限度的寫pure function,讓函數最大限度的減小side effect,而且保證函數在任什麼時候候傳遞相同參數時,獲得的結果都相同。

有什麼好處

當咱們按照函數式編程的思想編寫程序時,程序具備自然的模塊屬性,由於實現的函數爲pure function,你能夠任意組合這些pure function。

pure function不會產生side effect,不須要對外部的狀態產生顧慮。

其餘好處在不少文章中都有提到,後續有時間筆者再來分析。:-D

總結

但願經過前面幾個核心概念的講解,可以讓你明白函數式編程的本質,若是有任何文中寫的有誤差的地方,歡迎討論,👏

相關文章
相關標籤/搜索