函數式編程誕生於50多年前。如今愈來愈多的人開始接受並進行函數式編程的實踐。不只最古老的函數式語言 Lisp 重獲青春,並且新的函數式語言層出不窮,好比 Erlang、clojure、Scala、F#等等。目前最當紅的Objective-C, Python、Ruby、 Javascript都引入了對函數式編程的支持。就連老牌的面向對象的 Java、面向過程的 PHP, 以及蘋果最新的swift語言,都忙不迭地加入匿名函數等機制。編程
愈來愈多的跡象代表,函數式編程已經再也不是學術界的最愛,開始大踏步地在業界投入實用。 也許繼面向對象編程以後,函數式編程會成爲下一個編程的主流範式。swift
它屬於「結構化編程」的一種,主要思想是把運算過程儘可能寫成一系列嵌套的函數調用。說到函數式編程編程就不得不說面向對象。面向對象是把一個功能的一組操做和相關數據封裝在一個對象裏,面向對象是對象滿天飛。函數式編程是把一個功能的一個操做和相關數據封裝在一塊兒,函數式編程是函數滿天飛。函數式編程比面向對象的優點就是粒度更小,生命週期更短。減小bug的有效途徑就是減小變量的生命週期,縮小模塊的粒度;因此函數式編程更不容易引入bug。數組
是一種編程範型,它將計算機運算視爲數學上的函數計算,而且避免使用程序狀態以及易變對象。函數編程語言最重要的基礎是λ演算(lambda calculus)。λ演算中最關鍵的要素就是函數被看成變量處理,可以參與運算。併發
下面是一個輸出數組的例子 app
shoplist = ['apple','mango','carrot','banana'] print 'My shopping list is now', shoplist #輸出 #My shopping list is now ['banana', 'carrot', 'mango', 'rice']
這樣的代碼更易讀,代碼只是描述在幹什麼,而不是如何作到這點的具體實現。若是是過程式編程,須要一個for循環去描述實現細節。編程語言
函數式編程在解決複雜運算問題時,把一個問題分解爲若干子問題,逐步求解。軟件或程序的拼裝會變得更爲簡單和直觀。使代碼更容易理解,方便排查問題,而且具備更好的可維護性和擴展性。ide
如今有這樣一個數學表達式:模塊化
(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);
這個就是函數式編程的準則:函數不受外部變量影響,不依賴於外部變量,也不改變外部變量的值。
傳統的過程式編程:
int count; void increment() { returen count++; }
函數式編程:
def increment(count): return count+1;
函數不訪問全局變量,也不改變全局變量。
封裝、繼承、多態是面向對象編程的三大特性。函數式編程也有本身的語言特性。
數據的不可變性保證了程序是無狀態的,不少難解的bug每每是由各類複雜的狀態引發的。好比發現某些狀況下程序運行有問題,是某一個狀態引發的,可是這個狀態有100種可能性,在1000個地方都有對這個狀態進行操做。debug的時候要殺人的心都有了。數據不可變性同時保證了函數沒有「反作用」,函數的反作用是指除了返回函數值外,還對主調用函數
把函數做爲參數傳遞的例子
NSComparisonResult (^cmp)(id obj1, id obj2) = ^NSComparisonResult(id obj1, id obj2) { return [obj1 isEqualToString:obj2]; } NSArray* items = [@"a", @"c", @"d"]; [items sortedArrayUsingComparator:cmp];
sortedArrayUsingComparator方法負責排序,須要把比較的規則告訴他,將比較方法做爲一個參數傳到函數中進行運算。
Python代碼:
def toUpper(item) return item.upper() print map(tuUpper , [「hellow」,」world」])
將數組裏的全部字符串變爲大寫,直接使用map,不須要寫for循環。最後輸出 ["HELLO","WORLD"]
print reduce(lambda x , y : x + y,[1,2,3,4,5])
將數組裏全部數值進行累加,至關於1+2+3+4+5,輸出 15。
lambda是Python的匿名函數,lambda x,y:x+y至關於def func(x,y):return x+y
若是有一個需求找出一組數中的全部偶數並對他們求平方,最後求他們的和,能夠分解成三個步驟:
1)找偶數
2)求平方
3)累加
def even(nums): return filter(lambda x: x%2==0, nums) def square(nums): return map(lambda x: x*x, nums) def total(nums): return reduce(lambda x,y:x+y,nums) nums = [1,2,3,4,5,6,7,8,9,10] pipeline = total(square(even(nums)))
even方法求偶數,square求他們的平方,total方法將他們加在一塊兒。經過管道的方式把他們串聯在一塊兒,一個複雜的處理就完成了。
讓每一個方法只作一件事,並把這件事作到極致。
在其餘類型的語言中,變量每每用來保存狀態。變量不可變,意味着狀態不能保存在變量中。函數式編程使用參數保存狀態,最好的例子就是遞歸。
void fun(const int i) { if (i < 10 && i >= 0) { NSLog(@"i:%d", i); fun(i + 1); } }
每次狀態的變化就是值+1。
柯里化就是一個函數只有一個參數,那若是須要兩個參數怎麼辦,好比兩個數相加求和。經過把一個參數封裝成函數的方式實現。
def func(a): def add(b): return a+b return add funcA = func(5) print funcA(10)
func函數返回一個add函數,funcA變量就是一個a爲5的add函數。print funcA(10)就是向add函數傳入10,最後結果就是5+10 輸出15。
函數式編程大量使用函數,減小了代碼的重複,所以程序比較短,開發速度較快。
函數式編程注重幹什麼而不是怎麼幹,更容易理解。例子-----表達式(1 + 2) * 3 - 4,能夠寫成函數式語言:subtract(multiply(add(1,2), 3), 4)
函數式編程不依賴、也不會改變外界的狀態,只要給定輸入參數,返回的結果一定相同。所以,每個函數均可以被看作獨立單元,頗有利於進行單元測試 (unit testing)和除錯(debugging),以及模塊化組合。
函數式編程由於它不修改變量,因此根本不存在"鎖"線程的問題,不須要考慮"死鎖"(deadlock)。沒必要擔憂一個線程的數據,被另外一個線程修改,因此能夠很放心地把工做分攤到多個線程中。
示例2:
var s1 = Op1(); var s2 = Op2(); var s3 = concat(s1, s2);
因爲s1和s2互不干擾,不會修改變量,誰先執行是無所謂的,因此能夠放心地增長線程,把它們分配在兩個線程上完成。其餘類型的語言就作不到這一點,由於s1可能會修改系統狀態,而s2可能會用到這些狀態,因此必須保證s2在s1以後運行,天然也就不能部署到其餘線程上了。多核CPU是未來的潮流,因此函數式編程的這個特性很是重要。
函數式編程沒有反作用,只要保證接口不變,內部實現是外部無關的。因此,能夠在運行狀態下直接升級代碼,不須要重啓,也不須要停機。Erlang語言早就證實了這一點,它是瑞典愛立信公司爲了管理電話系統而開發的,電話系統的升級固然是不能停機的。
最後,其實使用面向對象或者面向方法都不重要,重要的是如何理解其中的價值觀和方法論,構造可維護、可擴展、穩定又靈活的程序。