霖呆呆的函數(shù)式編程之路(三)

只要一個(gè)實(shí)參

unary函數(shù)

先來看一個(gè)奇怪的例子:

// example1
["1","2","3"].map( parseFloat );
// [1,2,3]

["1","2","3"].map( parseInt );
// [1,NaN,NaN]

在上面的例子中梯醒,我想要將一組字符串全部設(shè)置成數(shù)字類型,但是在調(diào)用parseInt的時(shí)候,卻出現(xiàn)了這樣怪異的事情。這是為什么呢幻锁?

首先我們來說一下parseInt(str,radix)這個(gè)函數(shù),它接受兩個(gè)參數(shù)边臼,第一個(gè)是要被解析的字符串哄尔,第二個(gè)為可選參數(shù),表示要解析的數(shù)字的基數(shù)柠并。該值介于 2 ~ 36 之間,比如:

parseInt("10");         //返回 10
parseInt("19",10);      //返回 19 (10+9)
parseInt("11",2);       //返回 3 (2+1)
  • 當(dāng)參數(shù) radix 的值為 0岭接,或沒有設(shè)置該參數(shù)時(shí),parseInt() 會(huì)根據(jù) string 來判斷數(shù)字的基數(shù)臼予。
  • 如果該參數(shù)小于 2 或者大于 36鸣戴,則 parseInt() 將返回 NaN。

而在案例1中粘拾,由于我們在調(diào)用map的時(shí)候葵擎,函數(shù)時(shí)會(huì)傳入三個(gè)實(shí)參:valueindexlist半哟。parseInt()又會(huì)接收2個(gè)參數(shù),所以每次都相當(dāng)于是將valueindex傳入進(jìn)去了签餐。這樣就造成后面2個(gè)NaN的情況寓涨。

根據(jù)上面,我們可以看到氯檐,在實(shí)際開發(fā)中戒良,我們會(huì)面臨這樣一個(gè)問題:在設(shè)計(jì)一個(gè)實(shí)用函數(shù)傳入一個(gè)函數(shù),而這個(gè)實(shí)用函數(shù)會(huì)把多個(gè)實(shí)參傳入函數(shù)冠摄,但可能你只希望你的函數(shù)接收單一實(shí)參糯崎。如上面的parseInt()函數(shù),我在調(diào)用它的時(shí)候希望它每次只接收一個(gè)參數(shù)河泳。

根據(jù)上面的需要沃呢,我們是不是可以來實(shí)現(xiàn)這么一個(gè)簡單的實(shí)用函數(shù),它包裝一個(gè)函數(shù)調(diào)用拆挥,讓這個(gè)函數(shù)在每次調(diào)用的時(shí)候只接收一個(gè)參數(shù):

function unary(fn) {
    return function onlyOneArg(arg) {
        return fn(arg)
    }
}

// ES6
var unary = fn => 
                            arg => 
                                        fn(arg);

很簡單的一層封裝薄霜,也很好理解。

現(xiàn)在我們用它來配合上面的案例1:

["1","2","3"].map( parseFloat );
// [1,2,3]

["1","2","3"].map( parseInt );
// [1,NaN,NaN]

["1","2","3"].map( unary( parseInt ) );
// [1,2,3]

sum案例

了解了上面的unary函數(shù)之后,我們來看一個(gè)復(fù)雜一些的案例惰瓜。還是用map函數(shù)來進(jìn)行舉例否副,不過要用到我們在第二章講到的松散型柯里化looseCurry

        function looseCurry(fn, arity = fn.length) { // 松散型柯里化
            return (function nextCurried(prevArgs) {
                return function curried(...nextArgs) {
                    var args = prevArgs.concat(nextArgs)
                    if (args.length >= arity) {
                        return fn(...args)
                    } else {
                        return nextCurried(args)
                    }
                }
            })([])
        }
        function sum(...args) {
            var sum = 0;
            for (let i = 0; i < args.length; i++) {
                sum += args[i];
            }
            return sum;
        }
        let adder = looseCurry(sum, 2) // 第一步 傳2表示至少接收2個(gè)參數(shù)才可以
        let arr = [1, 2, 3, 4, 5]
        let arr2 = arr.map(adder(3)) // 第二步
        console.log(arr2)
        // 生成的結(jié)果很有意思
        // ["41,2,3,4,5", "61,2,3,4,5", "81,2,3,4,5", "101, ...

大家可以先將這段代碼在本地跑一下崎坊,并思考為什么會(huì)出現(xiàn)這樣的情況备禀???

其實(shí)原理很簡單:

在第一步的時(shí)候,創(chuàng)建adder函數(shù)奈揍,只有在接收2個(gè)及以上參數(shù)才會(huì)運(yùn)行sum()曲尸,

第二步的時(shí)候,由于map()會(huì)傳入3個(gè)實(shí)參:分別是value,indexlist打月,而松散型的柯里化是可以接收比預(yù)期(這里也就是2)多的參數(shù)的队腐。

所以在每次執(zhí)行sum函數(shù)的時(shí)候,實(shí)際傳入的都是3個(gè)參數(shù)奏篙,比如arr執(zhí)行第一次的時(shí)候:

第一次傳入:
firstArg: 3,   // 調(diào)用adder()時(shí)傳入的3
value: 1, // 數(shù)組第一項(xiàng)的值
index: 0, // 數(shù)組第一項(xiàng)的索引
list: [1, 2, 3, 4, 5] // 整個(gè)數(shù)組
// 前面3項(xiàng)相加為數(shù)字4柴淘,之后數(shù)字4與數(shù)組做字符串相加
=> 3 + 1 + 0 + [1, 2, 3, 4, 5]
=> "41,2,3,4,5"

第二次傳入
firstArg: 3,
value: 2,
index: 1,
list: [1, 2, 3, 4, 5]

此時(shí),使用我們的unary函數(shù)就可以解決上面的問題了:

let arr2 = arr.map( unary( adder(3) ) )
// [4,5,6,7,8]

傳一個(gè)返回一個(gè)

identity函數(shù)

說到只傳一個(gè)實(shí)參的函數(shù)秘通,在函數(shù)式編程工具庫中有另一種通用的基礎(chǔ)函數(shù):該函數(shù)接收一個(gè)實(shí)參为严,然后什么都不做,原封不動(dòng)地返回實(shí)參值肺稀。

function identity(v) {
    return v;
}

// ES6 箭頭函數(shù)形式
var identity =
    v =>
        v;

舉個(gè)例子第股,想象一下你要用正則表達(dá)式拆分(split up)一個(gè)字符串,但輸出的數(shù)組中可能包含一些空值话原。我們可以使用 filter(..) 數(shù)組方法(下文會(huì)詳細(xì)說到這個(gè)方法)來篩除空值夕吻,而我們將 identity(..) 函數(shù)作為 filter(..) 的斷言:

var words = "   Now is the time for all...  ".split( /\s|\b/ );
words;
// ["","Now","is","the","time","for","all","...",""]

words.filter( identity );
// ["Now","is","the","time","for","all","..."]

轉(zhuǎn)換函數(shù)的妙用

另一個(gè)使用 identity(..) 的示例就是將其作為替代一個(gè)轉(zhuǎn)換函數(shù)(譯者注:transformation,這里指的是對傳入值進(jìn)行修改或調(diào)整繁仁,返回新值的函數(shù))的默認(rèn)函數(shù):

function output(msg,formatFn = identity) {
    msg = formatFn( msg );
    console.log( msg );
}

function upper(txt) {
    return txt.toUpperCase();
}

output( "Hello World", upper );     // HELLO WORLD
output( "Hello World" );            // Hello World

上面的例子涉馅,相當(dāng)于是給output函數(shù)一個(gè)默認(rèn)的函數(shù),若是沒有傳的話黄虱,則原封不動(dòng)的返回傳進(jìn)來的msg稚矿。

恒定參數(shù)

Certain API

Certain API 禁止直接給方法傳值,而要求我們傳入一個(gè)函數(shù)捻浦,就算這個(gè)函數(shù)只是返回一個(gè)值晤揣。JS Promise 中的 then(..)方法就是一個(gè) Certain API。

then(..)中必須要傳入一個(gè)函數(shù)朱灿,而有時(shí)候我們可能不需要傳入一個(gè)有實(shí)際用處的函數(shù)昧识,而是直接返回then在上一步中獲取到的值。

或許你可以用ES6的箭頭函數(shù)解決這個(gè)問題:

p1.then( foo ).then( () => p2 ).then( bar )

在第二個(gè)then中直接將p2返回母剥。

constant函數(shù)

我們可以來構(gòu)造一個(gè)實(shí)用函數(shù)來實(shí)現(xiàn)上面的功能:

function constant(v) {
    return function value(){
        return v;
    };
}

// or the ES6 => form
var constant =
    v =>
        () =>
            v;

constant函數(shù)功能也很簡單滞诺,返回傳入的參數(shù)形导。

對比:

p1.then( foo ).then( () => p2 ).then( bar );

// 對比:

p1.then( foo ).then( constant( p2 ) ).then( bar );

兩種方式都可以解決Certain API的問題。但是我更建議用第二種方式习霹,該箭頭函數(shù)返回了一個(gè)來自外作用域的值朵耕,這和 函數(shù)式編程的理念有些矛盾。在后面“減少副作用”中會(huì)提到該行為的不足淋叶。

擴(kuò)展在參數(shù)中的妙用

在第一章中阎曹,我們提到了形參數(shù)組解構(gòu):

function foo( [x,y,...args] ) {
    console.log(x, y, args)
    // 1, 2, [3]
}

foo( [1,2,3] );

當(dāng)函數(shù)必須接收一個(gè)數(shù)組,而你卻想把數(shù)組內(nèi)容當(dāng)成單獨(dú)形參來處理的時(shí)候煞檩,這個(gè)技巧十分有用处嫌。

然而,有的時(shí)候斟湃,你無法改變原函數(shù)的定義熏迹,但想使用形參數(shù)組解構(gòu)。

比如下面這個(gè)例子:

function foo(x, y) {
    console.log( x + y );
}

function bar(fn) {
    fn( [ 3, 9 ] );
}

bar( foo );
// 3,9undefined

由于fn會(huì)將實(shí)參[3, 9]作為x傳入凝赛,那么y就是undefined注暗,所以達(dá)不到我們期望的效果。

在這種情況下墓猎,我們可能想要改變bar()函數(shù)的行為捆昏,將foo([3, 9])改為foo(…[3, 9]),這樣就能將 39 分別傳入 foo(..) 函數(shù)了毙沾。

為了調(diào)整一個(gè)函數(shù)骗卜,讓它能把接收的單一數(shù)組擴(kuò)展成各自獨(dú)立的實(shí)參,我們可以定義一個(gè)輔助函數(shù):

function spreadArgs(fn) {
    return function spreadFn(argsArr) {
        return fn( ...argsArr );
    };
}

// ES6 箭頭函數(shù)的形式:
var spreadArgs =
    fn =>
        argsArr =>
            fn( ...argsArr );

注意??

在我參考的教材中左胞,將這個(gè)輔助函數(shù)叫為spreadArgs寇仓,但一些庫,比如 Ramda烤宙,經(jīng)常把它叫做 apply(..)焚刺。

現(xiàn)在我們可以使用 spreadArgs(..) 來調(diào)整 foo(..) 函數(shù),使其作為一個(gè)合適的輸入?yún)?shù)并正常地工作:

bar( spreadArgs( foo ) );           // 12

本質(zhì)上门烂,spreadArgs(..) 函數(shù)使我們能夠定義一個(gè)借助數(shù)組 return 多個(gè)值的函數(shù),不過兄淫,它讓這些值仍然能分別作為其他函數(shù)的輸入?yún)?shù)來處理屯远。

一個(gè)函數(shù)的輸出作為另外一個(gè)函數(shù)的輸入被稱作組合(composition),這個(gè)在后面的章節(jié)中會(huì)詳細(xì)說明捕虽。

有了spreadArgs函數(shù)慨丐,同樣的我們也可以定義一個(gè)與它功能相反的函數(shù):

function gatherArgs(fn) {
    return function gatheredFn(...argsArr) {
        return fn( argsArr );
    };
}

// ES6 箭頭函數(shù)形式
var gatherArgs =
    fn =>
        (...argsArr) =>
            fn( argsArr );

在 Ramda 中,該實(shí)用函數(shù)被稱作 unapply(..)泄私,是與 apply(..) 功能相反的函數(shù)房揭。我認(rèn)為術(shù)語 “擴(kuò)展(spread)” 和 “聚集(gather)” 可以把這兩個(gè)函數(shù)發(fā)生的事情解釋得更好一些备闲。

不需要順序的柯里化和偏應(yīng)用

在上面介紹的多形參柯里化和偏應(yīng)用中,參數(shù)傳遞都是有一定順序的捅暴。哪一個(gè)參數(shù)在哪一步才能傳恬砂,放在第一個(gè)位置都是固定好的,要是想進(jìn)行修正調(diào)整可能需要費(fèi)一番功夫蓬痒。

這時(shí)候我們不得不思考有沒有一種方法能讓我們從修正參數(shù)順序這件事里解脫出來呢泻骤?

或許解構(gòu)模式可以?

在第一章節(jié)中梧奢,我們提到了命名參數(shù)解構(gòu)模式:

function foo( {x,y} = {} ) {
    console.log( x, y );
}

foo( {
    y: 3
} );                    // undefined 3

可以看到這種解構(gòu)模式就相當(dāng)于是一種映射狱掂,將調(diào)用時(shí)傳入的實(shí)參于函數(shù)的形參進(jìn)行一個(gè)映射。

命名實(shí)參主要的好處就是不用再糾結(jié)實(shí)參傳入的順序亲轨,因此提高了可讀性趋惨。

partialProps和curryProps

有了這樣的想法,我們就可以來重新調(diào)整一下原先的柯里化curry和偏應(yīng)用partial了:

function partialProps(fn, presetArgsObj) {
    return function partialApplied(laterArgsObj) {
        return fn(Object.assign({}, presetArgsObj, laterArgsObj))
    }
}
function curryProps(fn, arity = 1) {
    return (function nextCurried(prevArgsObj) {
        return function curried(nextArgObj = {}) {
            var [key] = Object.keys(nextArgObj)
            var allArgsObj = Object.assign({}, prevArgsObj, { [key]: nextArgObj[key] })
            if (Object.keys(allArgsObj).length >= arity) {
                return fn(allArgsObj);
            }
            else {
                return nextCurried(allArgsObj)
            }
        }
    })({})
}

我們甚至不需要設(shè)計(jì)一個(gè) partialPropsRight(..) 函數(shù)了惦蚊,因?yàn)槲覀兏静恍枰紤]屬性的映射順序器虾,通過命名來映射形參完全解決了我們有關(guān)于順序的煩惱!

現(xiàn)在可以來試試這兩個(gè)新函數(shù):

function foo({ x, y, z } = {}) {
    console.log( `x:${x} y:${y} z:${z}` );
}

var f1 = curryProps( foo, 3 );
var f2 = partialProps( foo, { y: 2 } );

f1( {y: 2} )( {x: 1} )( {z: 3} );
// x:1 y:2 z:3

f2( { z: 3, x: 1 } );
// x:1 y:2 z:3

我們不用再為參數(shù)順序而煩惱了养筒!現(xiàn)在曾撤,我們可以指定我們想傳入的實(shí)參,而不用管它們的順序如何晕粪。也不需要類似 reverseArgs(..) 的函數(shù)或其它妥協(xié)了挤悉。

屬性擴(kuò)展

上面的partialProps 看似解決了我們這種多形參無順序的問題,但是巫湘,只有在我們可以掌控 foo(..) 的函數(shù)簽名装悲,并且可以定義該函數(shù)的行為,使其解構(gòu)第一個(gè)參數(shù)的時(shí)候尚氛,以上技術(shù)才能起作用诀诊。

如果一個(gè)函數(shù),其形參是各自獨(dú)立的(沒有經(jīng)過形參解構(gòu))阅嘶,而且不能改變它的函數(shù)簽名属瓣,那我們應(yīng)該如何運(yùn)用這個(gè)技術(shù)呢?

function bar(x,y,z) {
    console.log( `x:${x} y:${y} z:${z}` );
}

比如上面的bar函數(shù)讯柔,接收的就是三個(gè)參數(shù)抡蛙,但是我想要能映射到對應(yīng)的位置,比如這樣傳:

bar({ y: 2, x: 1, z: 3 })

spreadArgProps函數(shù)

就像之前的 spreadArgs(..) 實(shí)用函數(shù)一樣魂迄,我們也可以定義一個(gè) spreadArgProps(..) 輔助函數(shù)粗截,它接收對象實(shí)參的 key: value 鍵值對,并將其 “擴(kuò)展” 成獨(dú)立實(shí)參捣炬。

為了滿足上面的需求熊昌,我們現(xiàn)在需要取得在調(diào)用fn時(shí)傳遞的實(shí)際參數(shù)绽榛。

JS的函數(shù)對象上有一個(gè)toString()方法,它返回函數(shù)代碼的字符串形式婿屹,其中包括函數(shù)聲明的簽名灭美。

function spreadArgProps(
    fn,
    propOrder =
        fn.toString()
        .replace( /^(?:(?:function.*\(([^]*?)\))|(?:([^\(\)]+?)\s*=>)|(?:\(([^]*?)\)\s*=>))[^]+$/, "$1$2$3" )
        .split( /\s*,\s*/ )
        .map( v => v.replace( /[=\s].*$/, "" ) )
) {
    return function spreadFn(argsObj) {
        return fn( ...propOrder.map( k => argsObj[k] ) );
    };
}

讓我們看看 spreadArgProps(..) 實(shí)用函數(shù)是怎么用的:

function bar(x,y,z) {
    console.log( `x:${x} y:${y} z:${z}` );
}

var f3 = curryProps( spreadArgProps( bar ), 3 );
var f4 = partialProps( spreadArgProps( bar ), { y: 2 } );

f3( {y: 2} )( {x: 1} )( {z: 3} );
// x:1 y:2 z:3

f4( { z: 3, x: 1 } );
// x:1 y:2 z:3

雖然上面的方法看上去有些不靠譜,但是它確實(shí)能解決我們實(shí)際的問題选泻,至少解決了80%的情況冲粤。

無形參風(fēng)格

在函數(shù)式編程中,還有一種流行的代碼風(fēng)格页眯,其目的是通過移除不必要的形參-實(shí)參映射來減少視覺上的干擾梯捕。這種風(fēng)格的正式名稱為 “隱性編程(tacit programming)”,一般則稱作 “無形參(point-free)” 風(fēng)格窝撵。術(shù)語 “point” 在這里指的是函數(shù)形參傀顾。

我們從一個(gè)簡單的例子開始:

function double(x) {
    return x * 2;
}

[1,2,3,4,5].map( function mapper(v){
    return double( v );
} );
// [2,4,6,8,10]

可以看到 mapper(..) 函數(shù)和 double(..) 函數(shù)有相同(或相互兼容)的函數(shù)簽名。形參(也就是所謂的 “point“)v 可以直接映射到 double(..) 函數(shù)調(diào)用里相應(yīng)的實(shí)參上碌奉。這樣短曾,mapper(..) 函數(shù)包裝層是非必需的。我們可以將其簡化為無形參風(fēng)格:

function double(x) {
    return x * 2;
}

[1,2,3,4,5].map( double );
// [2,4,6,8,10]

還有之前parseInt()的例子:

["1","2","3"].map( function mapper(v){
    return parseInt( v );
} );
// [1,2,3]

=> 無形參風(fēng)格:
["1","2","3"].map( unary( parseInt ) );
// [1,2,3]

借助unary()函數(shù)使得每次只傳一個(gè)參數(shù)赐劣。

not函數(shù)

首先來看一個(gè)案例:

function output(txt) {
    console.log( txt );
}

function printIf( predicate, msg ) {
    if (predicate( msg )) {
        output( msg );
    }
}

function isShortEnough(str) {
    return str.length <= 5;
}

var msg1 = "Hello";
var msg2 = msg1 + " World";

printIf( isShortEnough, msg1 );         // Hello
printIf( isShortEnough, msg2 );

案例很簡單嫉拐,我們要求當(dāng)信息足夠長時(shí),將它打印出來魁兼,換而言之婉徘,我們需要一個(gè) !isShortEnough(..) 斷言。你可能會(huì)首先想到:

function isLongEnough(str) {
    return !isShortEnough( str );
}

printIf( isLongEnough, msg1 );
printIf( isLongEnough, msg2 );          // Hello World

上面的方式看似是很簡單的咐汞,但是還是需要我們傳遞str盖呼,現(xiàn)在我們能否不通過重新實(shí)現(xiàn) str.length 的檢查邏輯,而重構(gòu)代碼并使其變成無形參風(fēng)格呢化撕?

我們定義一個(gè) not(..) 取反輔助函數(shù)(在函數(shù)式編程庫中又被稱作 complement(..)):

function not(predicate) {
    return function negated(...args){
        return !predicate( ...args );
    };
}

// ES6 箭頭函數(shù)形式
var not =
    predicate =>
        (...args) =>
            !predicate( ...args );

傳入的predicate為斷言几晤,也就是條件。

現(xiàn)在我們可以用not函數(shù)來修改上面的例子:

var isLongEnough = not( isShortEnough )

printIf( isLongEnough, msg2 )  // Hello World

when函數(shù)

到目前位置植阴,上面的案例已經(jīng)被我們優(yōu)化的不錯(cuò)了蟹瘾。但是也許還能再進(jìn)一步,我們實(shí)際上可以將 printIf(..) 函數(shù)本身重構(gòu)成無形參風(fēng)格掠手。

我們可以用 when(..) 實(shí)用函數(shù)來表示 if 條件句:

function when (predicate, fn) {
    return function conditional (...args) {
        if (predicate(...args)) {
            return fn(...args)
        }
    }
}
// ES6
var when = (predicate, fn) => 
                (...args) => 
                    predicate(...args) ? fn(...args) : undefined

我們把本章前面講到的另一些輔助函數(shù)和 when(..) 函數(shù)結(jié)合起來搞定無形參風(fēng)格的 printIf(..) 函數(shù):

var printIf = uncurry( partialRight( when, output ) );

我們是這么做的:將 output 方法右偏應(yīng)用為 when(..) 函數(shù)的第二個(gè)(fn 形參)實(shí)參热芹,這樣我們得到了一個(gè)仍然期望接收第一個(gè)實(shí)參(predicate 形參)的函數(shù)。當(dāng)該函數(shù)被調(diào)用時(shí)惨撇,會(huì)產(chǎn)生另一個(gè)期望接收(譯者注:需要被打印的)信息字符串的函數(shù),看起來就是這樣:fn(predicate)(str)府寒。

多個(gè)(兩個(gè))鏈?zhǔn)胶瘮?shù)的調(diào)用看起來很挫魁衙,就像被柯里化的函數(shù)报腔。于是我們用 uncurry(..) 函數(shù)處理它,得到一個(gè)期望接收 strpredicate 兩個(gè)實(shí)參的函數(shù)剖淀,這樣該函數(shù)的簽名就和 printIf(predicate,str) 原函數(shù)一樣了纯蛾。

printIf案例

現(xiàn)在我們可以將上面的printIf整理一下:

function output(msg) {
    console.log( msg );
}

function isShortEnough(str) {
    return str.length <= 5;
}

var isLongEnough = not( isShortEnough );

var printIf = uncurry( partialRight( when, output ) );

var msg1 = "Hello";
var msg2 = msg1 + " World";

printIf( isShortEnough, msg1 );         // Hello
printIf( isShortEnough, msg2 );

printIf( isLongEnough, msg1 );
printIf( isLongEnough, msg2 );          // Hello World

list案例

實(shí)現(xiàn)功能:

若是列表中的數(shù)大于3則添加進(jìn)greater,否則添加進(jìn)less纵隔。

這種簡單的案例雖然可以用十分簡單的方式來實(shí)現(xiàn)翻诉,但是為了習(xí)慣函數(shù)式編程的寫法,所以算是我做一個(gè)小練習(xí)吧捌刮。

var list = [1, 2, 3, 4, 5]
var greater = []
var less = []
var isGreater = (val) => val > 3;
var pushGreater = (val) => greater.push(val);
var pushLess = (val) => less.push(val);
var handleFn = fn => uncurry( partialRight( when, fn ) )
var setGreater = handleFn( pushGreater )
var setLess = handleFn( pushLess )
function handleList (list) {
    list.forEach(val => {
        setGreater(isGreater, val)
        setLess(not(isGreater), val)
    })
    console.log('greater', greater)
    console.log('less', less)
}
handleList(list)
// greater [4, 5]
// less [1, 2, 3]

總結(jié)

偏應(yīng)用是用來減少函數(shù)的參數(shù)數(shù)量 —— 一個(gè)函數(shù)期望接收的實(shí)參數(shù)量 —— 的技術(shù)碰煌,它減少參數(shù)數(shù)量的方式是創(chuàng)建一個(gè)預(yù)設(shè)了部分實(shí)參的新函數(shù)。

柯里化是偏應(yīng)用的一種特殊形式绅作,其參數(shù)數(shù)量降低為 1芦圾,這種形式包含一串連續(xù)的鏈?zhǔn)胶瘮?shù)調(diào)用,每個(gè)調(diào)用接收一個(gè)實(shí)參俄认。當(dāng)這些鏈?zhǔn)秸{(diào)用指定了所有實(shí)參時(shí)个少,原函數(shù)就會(huì)拿到收集好的實(shí)參并執(zhí)行。你同樣可以將柯里化還原眯杏。

其它類似 unary(..)夜焦、identity(..) 以及 constant(..) 的重要函數(shù)操作,是函數(shù)式編程基礎(chǔ)工具庫的一部分岂贩。

無形參是一種書寫代碼的風(fēng)格茫经,這種風(fēng)格移除了非必需的形參映射實(shí)參邏輯,其目的在于提高代碼的可讀性和可理解性河闰。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末科平,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子姜性,更是在濱河造成了極大的恐慌瞪慧,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件部念,死亡現(xiàn)場離奇詭異弃酌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)儡炼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門妓湘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乌询,你說我怎么就攤上這事榜贴。” “怎么了妹田?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵唬党,是天一觀的道長鹃共。 經(jīng)常有香客問我,道長驶拱,這世上最難降的妖魔是什么霜浴? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蓝纲,結(jié)果婚禮上阴孟,老公的妹妹穿的比我還像新娘。我一直安慰自己税迷,他們只是感情好永丝,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翁狐,像睡著了一般类溢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上露懒,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天闯冷,我揣著相機(jī)與錄音,去河邊找鬼懈词。 笑死蛇耀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坎弯。 我是一名探鬼主播纺涤,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抠忘!你這毒婦竟也來了撩炊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤崎脉,失蹤者是張志新(化名)和其女友劉穎拧咳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體囚灼,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骆膝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灶体。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阅签。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蝎抽,靈堂內(nèi)的尸體忽然破棺而出政钟,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布养交,位于F島的核電站衷戈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏层坠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一刁笙、第九天 我趴在偏房一處隱蔽的房頂上張望破花。 院中可真熱鬧,春花似錦疲吸、人聲如沸座每。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽峭梳。三九已至,卻和暖如春蹂喻,著一層夾襖步出監(jiān)牢的瞬間葱椭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工口四, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孵运,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓蔓彩,卻偏偏與公主長得像治笨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子赤嚼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容