柯里化

wxlworkhard發表於2018-03-12

定義

在電腦科學中,柯里化(英語:Currying),是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數而且返回結果的新函式的技術。

就是把 foo(x, y, z) 變成 foo(x)(y)(z)

為什麼需要柯里化

柯里化是一種程式設計的模式,並不是說某些功能只能用柯里化才能實現,而是說在一定的場景下使用柯里化使程式碼更加簡潔、程式碼複用度更高。

我們以“比較兩個數”的例子來說明。

方案一

程式碼如下:

enter image description here

判斷 1 是否大於 10 compare(1, 10, greaterThan) 我們有判斷是否大於 10 的需求,並且需要呼叫 n 次

compare(1, 10, greaterThan) 
compare(2, 10, greaterThan) 
compare(3, 10, greaterThan) 
......

這裡的引數 10、greaterThan 是重複程式碼的,我們優化一下。

方案二

function greaterThan10(value) {
    return compare(value, 10, greaterThan);
}
greaterThan10(1);
greaterThan10(2);
greaterThan10(3);
......

然後我們又有需求,判斷是否大於 11 且需要呼叫 n 次,按這種思路需要不斷寫 greaterThan* 方法,這是換了一種形式來寫重複程式碼,方案一與方案二沒有本質區別。

有沒有更簡潔的方式?答案是使用柯里化。

方案三

enter image description here

注意:這裡用的是 curry2 只支援兩個引數。

然後我的程式碼變為:

const greaterThan10 = greaterThan(10);
greaterThan10(1);
greaterThan10(2);
greaterThan10(3);
......

const greaterThan11 = greaterThan(11);
greaterThan11(1);
greaterThan11(2);
greaterThan11(3);
......

而且我們還能輕鬆實現判斷是否小於某數的功能,程式碼如下:

const lessThan10 = lessThan(10);
lessThan10(1);
lessThan10(2);
lessThan10(3);
......

lessThan(11)(1);

最後我們來對比下,看看使用柯里化的效果

方案一:

compare(1, 10, greaterThan);
compare(2, 10, greaterThan);             
compare(3, 10, greaterThan); 
......

方案三:

const greaterThan10 = greaterThan(10);
greaterThan10(1);
greaterThan10(2);
greaterThan10(3);
......

不使用柯里化時,三個引數為一個粒度;柯里化可以把三個引數分成三個粒度,在更細的粒度上實現程式碼的複用,複用度更高。

如何使用柯里化

上文中提到的 curry2 只支援兩個引數的情況,如果有更多的引數,不可能一直寫 curry3....n,可以使用 Lodash curry,支援任意引數個數。

注意 Lodash curry 方法不僅支援任意引數個數,還支援“部分應用”。

函數語言程式設計也經常看到部分應用,可以把部分應用理解成高階的柯里化,即 foo(x, y, z) 變成 foo(x)(y)(z)foo(x, y)(z)foo(x)(y, z)

Promise 柯里化

對於非同步的場景經常會出現如下程式碼:

enter image description here

成功的回撥和 catch 常常會有程式碼重複,能不能把這部分的邏輯統一處理,並且可以輕易擴充套件,允許定製化需求。

柯里化主要思想是高階函式的應用,在提供部分引數時返回一個新的函式,供其他引數繼續呼叫。根據柯里化的主要思想實現 Promise 的柯里化 pcurry 庫(傳統的柯里化方法並不適用非同步的情況)。

程式碼如下:

enter image description here

即支援統一的錯誤處理 decorate1,也可以定製化過濾器 decorate2,decorate2 複用了 decorate1 的邏輯。

pcurry 在接收一個函式引數時返回一個函式,並把函式新增到一個佇列 flow,在接收一個 promise 物件時按順序呼叫函式佇列 flow。

相關文章