自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

函數(shù)式編程初探

開發(fā) 開發(fā)工具
誕生50多年之后,函數(shù)式編程(functional programming)開始獲得越來越多的關(guān)注。

誕生50多年之后,函數(shù)式編程(functional programming)開始獲得越來越多的關(guān)注。

不僅最古老的函數(shù)式語言 Lisp 重獲青春,而且新的函數(shù)式語言層出不窮,比如 Erlang、clojure、Scala,、F#等等。目前最當(dāng)紅的 Python、Ruby、Javascript,對函數(shù)式編程的支持都很強(qiáng),就連老牌的面向?qū)ο蟮?Java、面向過程的 PHP,都忙不迭地加入對匿名函數(shù)的支持。越來越多的跡象表明,函數(shù)式編程已經(jīng)不再是學(xué)術(shù)界的最愛,開始大踏步地在業(yè)界投入實(shí)用。

也許繼"面向?qū)ο缶幊?quot;之后,"函數(shù)式編程"會成為下一個編程的主流范式(paradigm)。未來的程序員恐怕或多或少都必須懂一點(diǎn)。

[[84930]]

但是,"函數(shù)式編程"看上去比較難,缺乏通俗的入門教程,各種介紹文章都充斥著數(shù)學(xué)符號和專用術(shù)語,讓人讀了如墜云霧。就連最基本的問題"什么是函數(shù)式編程",網(wǎng)上都搜不到易懂的回答。

下面是我的"函數(shù)式編程"學(xué)習(xí)筆記,分享出來,與大家一起探討。內(nèi)容不涉及數(shù)學(xué)(Lambda Calculus 我也不懂),也不涉及高級特性(比如 lazy evaluation 和 currying),只求盡量簡單通俗地整理和表達(dá),我現(xiàn)在所理解的"函數(shù)式編程"以及它的意義。

我主要參考了 Slava Akhmechet 的"Functional Programming For The Rest of Us"。

一、定義

簡單說,"函數(shù)式編程"是一種"編程范式"(programming paradigm),也就是如何編寫程序的方法論。

它屬于"結(jié)構(gòu)化編程"的一種,主要思想是把運(yùn)算過程盡量寫成一系列嵌套的函數(shù)調(diào)用。舉例來說,現(xiàn)在有這樣一個數(shù)學(xué)表達(dá)式:

  1. (1 + 2) * 3 - 4 

傳統(tǒng)的過程式編程,可能這樣寫:

  1. var a = 1 + 2; 
  2.  
  3. var b = a * 3; 
  4.  
  5. var c = b - 4; 

函數(shù)式編程要求使用函數(shù),我們可以把運(yùn)算過程定義為不同的函數(shù),然后寫成下面這樣:

  1. var result = subtract (multiply (add (1,2), 3), 4); 

這就是函數(shù)式編程。

二、特點(diǎn)

函數(shù)式編程具有五個鮮明的特點(diǎn)。

1. 函數(shù)是"第一等公民"

所謂"第一等公民"(first class),指的是函數(shù)與其他數(shù)據(jù)類型一樣,處于平等地位,可以賦值給其他變量,也可以作為參數(shù),傳入另一個函數(shù),或者作為別的函數(shù)的返回值。

舉例來說,下面代碼中的 print 變量就是一個函數(shù),可以作為另一個函數(shù)的參數(shù)。

  1. var print = function (i){ console.log (i);}; 
  2.  
  3.    [1,2,3].forEach (print); 

2. 只用"表達(dá)式",不用"語句"

"表達(dá)式"(expression)是一個單純的運(yùn)算過程,總是有返回值;"語句"(statement)是執(zhí)行某種操作,沒有返回值。函數(shù)式編程要求,只使用表達(dá)式,不使用語句。也就是說,每一步都是單純的運(yùn)算,而且都有返回值。

原因是函數(shù)式編程的開發(fā)動機(jī),一開始就是為了處理運(yùn)算(computation),不考慮系統(tǒng)的讀寫(I/O)。"語句"屬于對系統(tǒng)的讀寫操作,所以就被排斥在外。

當(dāng)然,實(shí)際應(yīng)用中,不做I/O是不可能的。因此,編程過程中,函數(shù)式編程只要求把I/O限制到最小,不要有不必要的讀寫行為,保持計(jì)算過程的單純性。

3. 沒有"副作用"

所謂"副作用"(side effect),指的是函數(shù)內(nèi)部與外部互動(最典型的情況,就是修改全局變量的值),產(chǎn)生運(yùn)算以外的其他結(jié)果。

函數(shù)式編程強(qiáng)調(diào)沒有"副作用",意味著函數(shù)要保持獨(dú)立,所有功能就是返回一個新的值,沒有其他行為,尤其是不得修改外部變量的值。

4. 不修改狀態(tài)

上一點(diǎn)已經(jīng)提到,函數(shù)式編程只是返回新的值,不修改系統(tǒng)變量。因此,不修改變量,也是它的一個重要特點(diǎn)。

在其他類型的語言中,變量往往用來保存"狀態(tài)"(state)。不修改變量,意味著狀態(tài)不能保存在變量中。函數(shù)式編程使用參數(shù)保存狀態(tài),最好的例子就是遞歸。下面的代碼是一個將字符串逆序排列的函數(shù),它演示了不同的參數(shù)如何決定了運(yùn)算所處的"狀態(tài)"。

  1. function reverse (string) { 
  2.  
  3. if (string.length == 0) { 
  4.  
  5. return string; 
  6.  
  7. } else { 
  8.  
  9. return reverse (string.substring (1, string.length)) + string.substring (0, 1); 
  10.  
  11.  

由于使用了遞歸,函數(shù)式語言的運(yùn)行速度比較慢,這是它長期不能在業(yè)界推廣的主要原因。

5. 引用透明

引用透明(Referential transparency),指的是函數(shù)的運(yùn)行不依賴于外部變量或"狀態(tài)",只依賴于輸入的參數(shù),任何時候只要參數(shù)相同,引用函數(shù)所得到的返回值總是相同的。

有了前面的第三點(diǎn)和第四點(diǎn),這點(diǎn)是很顯然的。其他類型的語言,函數(shù)的返回值往往與系統(tǒng)狀態(tài)有關(guān),不同的狀態(tài)之下,返回值是不一樣的。這就叫"引用不透明",很不利于觀察和理解程序的行為。

三、意義

函數(shù)式編程到底有什么好處,為什么會變得越來越流行?

1. 代碼簡潔,開發(fā)快速

函數(shù)式編程大量使用函數(shù),減少了代碼的重復(fù),因此程序比較短,開發(fā)速度較快。

Paul Graham 在《黑客與畫家》一書中寫道:同樣功能的程序,極端情況下,Lisp 代碼的長度可能是C代碼的二十分之一。

如果程序員每天所寫的代碼行數(shù)度基本相同,這就意味著,"C語言需要一年時間完成開發(fā)某個功能,Lisp 語言只需要不到三星期。反過來說,如果某個新功能,Lisp 語言完成開發(fā)需要三個月,C語言需要寫五年。"當(dāng)然,這樣的對比故意夸大了差異,但是"在一個高度競爭的市場中,即使開發(fā)速度只相差兩三倍,也足以使得你 永遠(yuǎn)處在落后的位置。"

2. 接近自然語言,易于理解

函數(shù)式編程的自由度很高,可以寫出很接近自然語言的代碼。

前文曾經(jīng)將表達(dá)式(1 + 2) * 3 - 4,寫成函數(shù)式語言:

  1. subtract (multiply (add (1,2), 3), 4) 

對它進(jìn)行變形,不難得到另一種寫法:

  1. add (1,2) .multiply (3) .subtract (4) 

這基本就是自然語言的表達(dá)了。再看下面的代碼,大家應(yīng)該一眼就能明白它的意思吧:

  1. merge ([1,2],[3,4]) .sort () .search ("2") 

因此,函數(shù)式編程的代碼更容易理解。

3. 更方便的代碼管理

函數(shù)式編程不依賴、也不會改變外界的狀態(tài),只要給定輸入?yún)?shù),返回的結(jié)果必定相同。因此,每一個函數(shù)都可以被看做獨(dú)立單元,很有利于進(jìn)行單元測試(unit testing)和除錯(debugging),以及模塊化組合。

4. 易于"并發(fā)編程"

函數(shù)式編程不需要考慮"死鎖"(deadlock),因?yàn)樗恍薷淖兞?,所以根本不存?quot;鎖"線程的問題。不必?fù)?dān)心一個線程的數(shù)據(jù),被另一個線程修改,所以可以很放心地把工作分?jǐn)偟蕉鄠€線程,部署"并發(fā)編程"(concurrency)。

請看下面的代碼:

  1. var s1 = Op1(); 
  2.  
  3. var s2 = Op2(); 
  4.  
  5. var s3 = concat (s1, s2); 

由于 s1 和 s2 互不干擾,不會修改變量,誰先執(zhí)行是無所謂的,所以可以放心地增加線程,把它們分配在兩個線程上完成。其他類型的語言就做不到這一點(diǎn),因?yàn)?s1 可能會修改系統(tǒng)狀態(tài),而 s2 可能會用到這些狀態(tài),所以必須保證 s2 在 s1 之后運(yùn)行,自然也就不能部署到其他線程上了。

多核CPU是將來的潮流,所以函數(shù)式編程的這個特性非常重要。

5. 代碼的熱升級

函數(shù)式編程沒有副作用,只要保證接口不變,內(nèi)部實(shí)現(xiàn)是外部無關(guān)的。所以,可以在運(yùn)行狀態(tài)下直接升級代碼,不需要重啟,也不需要停機(jī)。Erlang 語言早就證明了這一點(diǎn),它是瑞典愛立信公司為了管理電話系統(tǒng)而開發(fā)的,電話系統(tǒng)的升級當(dāng)然是不能停機(jī)的。

原文鏈接:http://www.ruanyifeng.com/blog/2012/04/functional_programming.html

責(zé)任編輯:陳四芳 來源: ruanyifeng.com
相關(guān)推薦

2009-07-08 16:52:29

ScalaScala教程

2011-03-08 15:47:32

函數(shù)式編程

2016-10-31 20:46:22

函數(shù)式編程Javascript

2025-03-11 10:00:20

Golang編程函數(shù)

2020-09-24 10:57:12

編程函數(shù)式前端

2023-12-14 15:31:43

函數(shù)式編程python編程

2011-08-24 09:13:40

編程

2022-09-22 08:19:26

WebFlux函數(shù)式編程

2017-06-08 14:25:46

Kotlin函數(shù)

2010-11-25 09:06:37

Web開發(fā)函數(shù)式編程

2010-03-11 10:34:22

Scala

2012-09-21 09:21:44

函數(shù)式編程函數(shù)式語言編程

2020-09-23 07:50:45

Java函數(shù)式編程

2020-09-22 11:00:11

Java技術(shù)開發(fā)

2016-08-11 10:11:07

JavaScript函數(shù)編程

2016-08-11 10:34:37

Javascript函數(shù)編程

2022-07-07 09:03:36

Python返回函數(shù)匿名函數(shù)

2019-09-09 11:40:18

編程函數(shù)開發(fā)

2009-08-24 09:46:40

面向切面編程AOP

2022-10-25 14:05:47

共識算法系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號