函數(shù)式編程這幾年變得越來(lái)越流行起來(lái),越來(lái)越多的語(yǔ)音融入了函數(shù)式編程的語(yǔ)法,就連更新緩慢如Java也不例外,在Java8中引入了函數(shù)式編程的語(yǔ)法袍祖。JVM平臺(tái)的后起之秀Scala,Groovy更是出生之初就有內(nèi)置了對(duì)函數(shù)式編程的支持谢揪。作為最經(jīng)常被拿來(lái)與Java做比較的C#蕉陋,更是搶先一步在.NET 3.5版本時(shí)就有了對(duì)函數(shù)式編程的支持。后來(lái).NET平臺(tái)上更是直接引入了函數(shù)式編程語(yǔ)言F#拨扶。
函數(shù)式編程的興起并非沒(méi)有原因凳鬓。
- 函數(shù)式編程語(yǔ)言一般極具表現(xiàn)力. 相比主流的面向?qū)ο笳Z(yǔ)言能以極少的代碼完成相同的工作。與代碼量的減少相對(duì)應(yīng)的就是維護(hù)成本的降低患民。
- 相對(duì)于命令式編程缩举,函數(shù)式編程語(yǔ)言都是采用聲明式編程的方式,能在更高的抽象級(jí)別上編程匹颤,代碼更易于理解仅孩。
- 純函數(shù)是沒(méi)有副作用的,既不會(huì)修改全局狀態(tài)印蓖,也不會(huì)修改傳入的參數(shù)辽慕。在進(jìn)行多線程編程時(shí),就從根本上避免死鎖赦肃,活鎖或者是線程饑餓的問(wèn)題溅蛉。
函數(shù)式編程語(yǔ)言的特征
高階函數(shù)
在函數(shù)式編程語(yǔ)言中,函數(shù)終于成為一等公民他宛,可以跟變量一樣作為參數(shù)傳遞船侧,同時(shí)也可以作為函數(shù)返回值返回。這個(gè)功能帶來(lái)的直接好處就是當(dāng)我們需要傳遞行為的時(shí)候厅各,不必在跟之前一樣為了傳遞行為而引入一個(gè)對(duì)象镜撩。例如在Java 8之前,當(dāng)我們想排序一個(gè)List時(shí):
List<Integer> values = newArrayList(11, 2, 43, 14, 5, 76, 6);
Collections.sort(values, new Comparator<Integer>() {
@Override
public int compare(Integer one, Integer other) {
return one.compareTo(other);
}
});
Java 8引入了Lambda之后队塘,我們不必再引入一個(gè)對(duì)象來(lái)封裝我們的排序邏輯:
Collections.sort(values, (a, b) -> a.compareTo(b));
本質(zhì)上講琐鲁,傳遞進(jìn)來(lái)的還是一個(gè)對(duì)象卫旱,只不過(guò)是因?yàn)镕unctional Interface的存在,我們可以假裝傳入的是一個(gè)函數(shù)围段。這一點(diǎn)在我們使用傳入的對(duì)象時(shí)就更為明顯: 我們不得不像使用對(duì)象一樣通過(guò)調(diào)用apply方法來(lái)應(yīng)用傳入的函數(shù)。
public static <T> T create(String stringValue, Function<String, T> instantiator) {
return instantiator.apply(stringValue);
}
在純粹的函數(shù)式編程語(yǔ)言中投放,高階函數(shù)的使用則更為自然奈泪, 如在haskell中,
add :: a -> a -> a
add a b = a + b
perform :: a -> a -> (a -> a -> a) -> a
perform a b action = action a b
let result = perform 1 1 add
// result = 2
既然函數(shù)可以作為參數(shù)傳遞灸芳,也可以作為返回值涝桅,那么函數(shù)之間的運(yùn)算也就不足為奇了。以下是Haskell中用來(lái)組合(compose)函數(shù)的函數(shù)(對(duì)烙样,這里沒(méi)有寫錯(cuò)冯遂,就是通過(guò)組合函數(shù)來(lái)生成更強(qiáng)大函數(shù))。
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f(g(x))
利用(.)谒获,我們可以對(duì)函數(shù)進(jìn)行組合:
plus :: a -> a
plus x = x + 1
double :: a -> a
double x = x * 2
plusThenDouble = double.plus
let result = plusThenDouble 1
// result = 4
科里化(Currying)
科里化把一個(gè)(接收多個(gè)參數(shù)的函數(shù))的運(yùn)算轉(zhuǎn)化為多個(gè)(只接收一個(gè)參數(shù)的函數(shù))的運(yùn)算蛤肌。例如:
add :: a -> a -> a
add a b = a + b
函數(shù)add接收兩個(gè)參數(shù),返回兩者的和批狱。我們可以把函數(shù)add理解為接收一個(gè)參數(shù)a裸准,然后返回一個(gè)函數(shù)addA。函數(shù)B接收另外一個(gè)參數(shù)B, 返回值則是A+B赔硫。那么1+2的例子就可以如下所示:
add1 :: a -> a
add1 = add 1
let result = add1 2 // result = 3
上面的例子中add1其實(shí)就是一個(gè)部分應(yīng)用函數(shù)(Partial Applied Function)炒俱。
其實(shí)這就是函數(shù)式編程語(yǔ)言中代碼重用的方式。面向?qū)ο笳Z(yǔ)言通過(guò)繼承和組合重用已有邏輯爪膊,函數(shù)式語(yǔ)言可以通過(guò)部分應(yīng)用函數(shù)以及函數(shù)組合來(lái)實(shí)現(xiàn)代碼復(fù)用权悟。
本篇文章是函數(shù)式編程系列之一:
- 函數(shù)式編程 101
- 函數(shù)式編程 101 (續(xù))
- 函數(shù)式編程初體驗(yàn) 一
- 函數(shù)式編程初體驗(yàn) 二
- 函數(shù)式編程初體驗(yàn) 三