函數式編程(三)

前面兩節介紹了純函數和高階函數,在函數式編程中,函數就是咱們的磚塊。咱們編寫一些能夠完成很是具體任務的函數,而後像樂高積木同樣將他們搭建起來。編程

這就是所謂的函數組合。數組

函數組合

先看兩個純函數緩存

let add10 = value => value + 10;
let mult5 = value => value * 5;

let mult5AfterAdd10 = value => mult5(add10(value))

這樣寫代碼很容易寫出h(g(f(e(x)))),這樣層層相套的代碼,一層一層撥開它的心。
爲了解函數嵌套的問題,咱們能夠先預約義一個組合函數app

var compose = function(f,g) {
  return function(x) {
    return f(g(x));
  };
};

而後改寫上面的例子函數式編程

let mult5AfterAdd10 = compose(mult5, add10)
mult5AfterAdd10(5)

這樣作的可讀性遠遠高於嵌套一大堆的函數調用。函數

Point Free

把一些對象自帶的方法轉化爲純函數,不要命名轉瞬即逝的中間變量this

網上有不少關於Point Free的案例,我認爲都不怎麼能說明問題,下面這個例子是我精心準備的機密,通常不外傳。prototype

const f = str => str.toUpperCase().split('');
const toUpperCase = word => word.toUpperCase();
const split = x => (str => str.split(x));


const f = compose(split(''), toUpperCase)

f("aa vv")
//咱們沒有使用到str變量

咱們不須要指定冗餘的參數。因爲沒必要指定參數,因此也就沒必要考慮爲它們命名。其次,因爲更簡短使得更容易閱讀。Pointfree 的本質就是使用一些通用的函數,組合出各類複雜運算。上層運算不要直接操做數據,而是經過底層函數去處理。這就要求,將一些經常使用的操做封裝成函數。因此說這種方式慎用。code

看到這裏你是否是覺得本身就瞭解函數式編程了呢?nonono?下面我有個問題想考考你。對象

let add = (value, y) => value + y;
let mult5 = value => value * 5;

我想改寫上面的mult5AfterAdd10函數,把它轉成更通用的mult5AfterAdd,你想到用上面學到的姿式,

let mult5AfterAdd = compose(mult5, add)

顯然這個不行的,由於add函數須要接收兩個參數,實際只傳遞了一個參數,因此它會將一個錯誤的結果傳遞給mult5。這最終會產生一個錯誤的結果。
咱們怎麼解決這個問題?事實證實有一種方法,它就是柯里化(Currying)

下面咱們來看看函數的柯里化。
有這樣一道題目,實現一個函數,實現以下功能:

var result = sum(1)(2)(3);
console.log(result);//6

如下是一種實現方式

function add(a){
    var sum = 0;
        sum += a;
    return b => {
        sum += b;
        return c => {
            sum += c;
            return sum;
        }
    }
}
add(1)(2)(3);//6

咱們來解決上面遺留的問題,經過改寫add函數

let add = x => y => x + y
let compose = (f, g) => x => f(g(x));
let mult5AfterAdd10 = compose(mult5, add(10));

咱們就是將簡單常見的add函數轉化成了柯里化函數,這樣add函數就變得更加自由靈活了。咱們先將第1個參數10輸入,而當mult5AfterAdd10函數被調用的時候,最後1個參數纔有了肯定的值。

柯里化

柯里化一般也稱部分求值,其含義是給函數分步傳遞參數,每次傳遞參數後,部分應用參數,並返回一個更具體的函數接受剩下的參數,中間可嵌套多層這樣的接受部分參數函數,逐步縮小函數的適用範圍,逐步求解,直至返回最後結果。

一個簡單的通用模塊的函數柯里化封裝

const curry = fn => {
    const _args = [];
    return function cb() {

        if(arguments.length === 0) {
            return fn.apply(this, _args);
        }

        Array.prototype.push.apply(_args, [...arguments]);

        return cb;
    }
}

函數柯里化是一種預加載函數的方法,經過傳遞較少的參數,獲得一個已經記住了這些參數的新函數,某種意義上來說,這是一種對參數的「緩存」,是一種很是高效的編寫函數的方法。⚠️參數順序是可否最大程度利用柯里化的關鍵所在。

相關文章
相關標籤/搜索