只要一個(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í)參:value
、index
和 list
半哟。parseInt()
又會(huì)接收2個(gè)參數(shù),所以每次都相當(dāng)于是將value
和index
傳入進(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
,index
和list
打月,而松散型的柯里化是可以接收比預(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])
,這樣就能將 3
和 9
分別傳入 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è)期望接收str
和predicate
兩個(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í)參邏輯,其目的在于提高代碼的可讀性和可理解性河闰。