讀了幾篇關於函數式編程的文章, 來粗淺記錄下本身的理解:javascript
對比OOP,FP的核心就是一切都是函數,也就是說」活「的再也不是對象,而是函數。函數本質是x到f(x)的變換,因此FP的元素就兩種,一種是函數(」pure function「的概念,表現爲命名句柄或表達式,因此獨立性->併發性),一種是狀態(immutable,由於只要發生變換,就經過函數表現)。 函數的獨立性,致使了咱們無法記錄函數內部運行的狀態,怎麼解決呢,把狀態放在參數裏唄,而後用遞歸來搞定。html
給出msdn裏imperative (procedural) programming和 functional programming 的對比:(https://msdn.microsoft.com/en-us/library/bb669144.aspx)java
The functional programming paradigm was explicitly created to support a pure functional approach to problem solving. Functional programming is a form of declarative programming. In contrast, most mainstream languages, including object-oriented programming (OOP) languages such as C#, Visual Basic, C++, and Java –, were designed to primarily support imperative programming.程序員
With an imperative approach, a developer writes code that describes in exacting detail the steps that the computer must take to accomplish the goal. This is sometimes referred to as algorithmic programming. In contrast, a functional approach involves composing the problem as a set of functions to be executed. You define carefully the input to each function, and what each function returns. The following table describes some of the general differences between these two approaches.express
Characteristic編程 |
Imperative approach併發 |
Functional approachapp |
---|---|---|
Programmer focuside |
How to perform tasks (algorithms) and how to track changes in state.模塊化 |
What information is desired and what transformations are required. |
State changes |
Important. |
Non-existent. |
Order of execution |
Important. |
Low importance. |
Primary flow control |
Loops, conditionals, and function (method) calls. |
Function calls, including recursion. |
Primary manipulation unit |
Instances of structures or classes. |
Functions as first-class objects and data collections. |
Although most languages were designed to support a specific programming paradigm, many general languages are flexible enough to support multiple paradigms. For example, most languages that contain function pointers can be used to credibly support functional programming. Furthermore, in C# 3.0 and Visual Basic 9.0, explicit language extensions have been added to support functional programming, including lambda expressions and type inference. LINQ technology is a form of declarative, functional programming.
以爲有一篇比較適合初學者理解,因此分享下,原文地址:
http://www.ruanyifeng.com/blog/2012/04/functional_programming.html
原文:
誕生50多年以後,函數式編程(functional programming)開始得到愈來愈多的關注。
不只最古老的函數式語言Lisp重獲青春,並且新的函數式語言層出不窮,好比Erlang、clojure、Scala、F#等等。目前最當紅的Python、Ruby、Javascript,對函數式編程的支持都很強,就連老牌的面向對象的Java、面向過程的PHP,都忙不迭地加入對匿名函數的支持。愈來愈多的跡象代表,函數式編程已經再也不是學術界的最愛,開始大踏步地在業界投入實用。
也許繼"面向對象編程"以後,"函數式編程"會成爲下一個編程的主流範式(paradigm)。將來的程序員恐怕或多或少都必須懂一點。
可是,"函數式編程"看上去比較難,缺少通俗的入門教程,各類介紹文章都充斥着數學符號和專用術語,讓人讀了如墜雲霧。就連最基本的問題"什麼是函數式編程",網上都搜不到易懂的回答。
下面是個人"函數式編程"學習筆記,分享出來,與你們一塊兒探討。內容不涉及數學(我也不懂Lambda Calculus),也不涉及高級特性(好比lazy evaluation和currying),只求儘可能簡單通俗地整理和表達,我如今所理解的"函數式編程"以及它的意義。
我主要參考了Slava Akhmechet的"Functional Programming For The Rest of Us"。
1、定義
簡單說,"函數式編程"是一種"編程範式"(programming paradigm),也就是如何編寫程序的方法論。
它屬於"結構化編程"的一種,主要思想是把運算過程儘可能寫成一系列嵌套的函數調用。舉例來講,如今有這樣一個數學表達式:
(1 + 2) * 3 - 4
傳統的過程式編程,可能這樣寫:
var a = 1 + 2;
var b = a * 3;
var c = b - 4;
函數式編程要求使用函數,咱們能夠把運算過程定義爲不一樣的函數,而後寫成下面這樣:
var result = subtract(multiply(add(1,2), 3), 4);
這就是函數式編程。
2、特色
函數式編程具備五個鮮明的特色。
1. 函數是"第一等公民"
所謂"第一等公民"(first class),指的是函數與其餘數據類型同樣,處於平等地位,能夠賦值給其餘變量,也能夠做爲參數,傳入另外一個函數,或者做爲別的函數的返回值。
舉例來講,下面代碼中的print變量就是一個函數,能夠做爲另外一個函數的參數。
var print = function(i){ console.log(i);};
[1,2,3].forEach(print);
2. 只用"表達式",不用"語句"
"表達式"(expression)是一個單純的運算過程,老是有返回值;"語句"(statement)是執行某種操做,沒有返回值。函數式編程要求,只使用表達式,不使用語句。也就是說,每一步都是單純的運算,並且都有返回值。
緣由是函數式編程的開發動機,一開始就是爲了處理運算(computation),不考慮系統的讀寫(I/O)。"語句"屬於對系統的讀寫操做,因此就被排斥在外。
固然,實際應用中,不作I/O是不可能的。所以,編程過程當中,函數式編程只要求把I/O限制到最小,不要有沒必要要的讀寫行爲,保持計算過程的單純性。
3. 沒有"反作用"
所謂"反作用"(side effect),指的是函數內部與外部互動(最典型的狀況,就是修改全局變量的值),產生運算之外的其餘結果。
函數式編程強調沒有"反作用",意味着函數要保持獨立,全部功能就是返回一個新的值,沒有其餘行爲,尤爲是不得修改外部變量的值。
4. 不修改狀態
上一點已經提到,函數式編程只是返回新的值,不修改系統變量。所以,不修改變量,也是它的一個重要特色。
在其餘類型的語言中,變量每每用來保存"狀態"(state)。不修改變量,意味着狀態不能保存在變量中。函數式編程使用參數保存狀態,最好的例子就是遞歸。下面的代碼是一個將字符串逆序排列的函數,它演示了不一樣的參數如何決定了運算所處的"狀態"。
function reverse(string) {
if(string.length == 0) {
return string;
} else {
return reverse(string.substring(1, string.length)) + string.substring(0, 1);
}
}
因爲使用了遞歸,函數式語言的運行速度比較慢,這是它長期不能在業界推廣的主要緣由。
5. 引用透明
引用透明(Referential transparency),指的是函數的運行不依賴於外部變量或"狀態",只依賴於輸入的參數,任什麼時候候只要參數相同,引用函數所獲得的返回值老是相同的。
有了前面的第三點和第四點,這點是很顯然的。其餘類型的語言,函數的返回值每每與系統狀態有關,不一樣的狀態之下,返回值是不同的。這就叫"引用不透明",很不利於觀察和理解程序的行爲。
3、意義
函數式編程到底有什麼好處,爲何會變得愈來愈流行?
1. 代碼簡潔,開發快速
函數式編程大量使用函數,減小了代碼的重複,所以程序比較短,開發速度較快。
Paul Graham在《黑客與畫家》一書中寫道:一樣功能的程序,極端狀況下,Lisp代碼的長度多是C代碼的二十分之一。
若是程序員天天所寫的代碼行數基本相同,這就意味着,"C語言須要一年時間完成開發某個功能,Lisp語言只須要不到三星期。反過來講,若是某個新功能,Lisp語言完成開發須要三個月,C語言須要寫五年。"固然,這樣的對比故意誇大了差別,可是"在一個高度競爭的市場中,即便開發速度只相差兩三倍,也足以使得你永遠處在落後的位置。"
2. 接近天然語言,易於理解
函數式編程的自由度很高,能夠寫出很接近天然語言的代碼。
前文曾經將表達式(1 + 2) * 3 - 4,寫成函數式語言:
subtract(multiply(add(1,2), 3), 4)
對它進行變形,不可貴到另外一種寫法:
add(1,2).multiply(3).subtract(4)
這基本就是天然語言的表達了。再看下面的代碼,你們應該一眼就能明白它的意思吧:
merge([1,2],[3,4]).sort().search("2")
所以,函數式編程的代碼更容易理解。
3. 更方便的代碼管理
函數式編程不依賴、也不會改變外界的狀態,只要給定輸入參數,返回的結果一定相同。所以,每個函數均可以被看作獨立單元,頗有利於進行單元測試(unit testing)和除錯(debugging),以及模塊化組合。
4. 易於"併發編程"
函數式編程不須要考慮"死鎖"(deadlock),由於它不修改變量,因此根本不存在"鎖"線程的問題。沒必要擔憂一個線程的數據,被另外一個線程修改,因此能夠很放心地把工做分攤到多個線程,部署"併發編程"(concurrency)。
請看下面的代碼:
var s1 = Op1();
var s2 = Op2();
var s3 = concat(s1, s2);
因爲s1和s2互不干擾,不會修改變量,誰先執行是無所謂的,因此能夠放心地增長線程,把它們分配在兩個線程上完成。其餘類型的語言就作不到這一點,由於s1可能會修改系統狀態,而s2可能會用到這些狀態,因此必須保證s2在s1以後運行,天然也就不能部署到其餘線程上了。
多核CPU是未來的潮流,因此函數式編程的這個特性很是重要。
5. 代碼的熱升級
函數式編程沒有反作用,只要保證接口不變,內部實現是外部無關的。因此,能夠在運行狀態下直接升級代碼,不須要重啓,也不須要停機。Erlang語言早就證實了這一點,它是瑞典愛立信公司爲了管理電話系統而開發的,電話系統的升級固然是不能停機的。
(完)