写一段可以无限被重用的代码,对于程序员来说无异于黄粱美梦。代码表达清晰是因为代码要表达需求,代码易于重用……好吧,只是因为你要重复使用它。两者不可得兼,你还能奢求更多么?
柯里化可以帮上忙。
通常, JavaScript 函数看起来都像这样:
var add = function(a, b){ return a + b }
add(1, 2) //= 3
它接受一些参数,然后有一个返回值。我可以使用过多(多余的参数会被忽略)或过少(会给出奇怪的返回值)的参数调用它:
add(1, 2, 'IGNORE ME') //= 3
add(1) //= NaN
柯里化可以使一个多参数函数转化为一系列单参数函数。比如,柯里化后的加法函数:
var curry = require('curry');
var add = curry(function(a, b){ return a + b })
var add100 = add(100)
add100(1) //= 101
柯里化后的多参数函数可以如下调用:
var sum3 = curry(function(a, b, c){ return a + b + c })
sum3(1)(2)(3) //= 6
这样的写法在 JavaScript 中可能有点丑,所以柯里化也允许你一次传入都个参数:
var sum3 = curry(function(a, b, c){ return a + b + c })
sum3(1, 2, 3) //= 6
sum3(1)(2, 3) //= 6
sum3(1, 2)(3) //= 6
如果你对于那些经常使用柯里化函数的语言很熟(比如Haskell),可能看不出来这样做会带来什么好处。在我的理解中,主要有以下两点好处:
举一个比较明显的例子;从一个集合中获取所有成员的 id :
var objects = [{ id: 1 }, { id: 2 }, { id: 3 }]
objects.map(function(o){ return o.id })
如果你正在厘清第二行代码的真实逻辑,让我帮你把它择出来吧:
MAP over OBJECTS to get IDS (遍历所有对象获取它们的 ID 值)
从函数定义的形式来看,只是这一行就有很多垃圾代码。让我们将其整理干净:
var get = curry(function(property, object){ return object[property] })
objects.map(get('id')) //= [1, 2, 3]
现在我们可以照着代码讲出真实的逻辑了 - 遍历所有对象获取它们的 ID 值。我们创建的get 方法是一个可部分配置的方法。
如果我们想要重用‘从一组对象中获取 id ’这个功能怎么办?最直接的办法就是:
var getIDs = function(objects){
return objects.map(get('id'))
}
getIDs(objects) //= [1, 2, 3]
嗯,看起来我们又丢掉了优雅简练,退回到杂乱无章的编码风格了。那我们该怎么办呢?额 - 如果 map 方法可以在还没有数据的时候,用一个函数进行部分配置的话?
var map = curry(function(fn, value){ return value.map(fn) })
var getIDs = map(get('id'))
getIDs(objects) //= [1, 2, 3]
看起来,如果我们以柯里化函数作为基础构建模块,我们就可以使用它们很容易得创造出全新的功能函数。更让人兴奋的是,代码看起来更能体现你的业务逻辑。
使用这种方式写代码还有另一个好处,它鼓励函数的使用;而不是类的方法。虽然类的方法是很美好的 —— 允许多态,代码可读性高 —— 但它们并不总适合所有的工作,比如拥有大量异步调用的时候。
下面的例子中,我们会从服务器端获取数据,再将其进行处理。数据形式如下:
{
"user": "hughfdjackson",
"posts": [
{ "title": "why curry?", "contents": "..." },
{ "title": "prototypes: the short(est possible) story", "contents": "..." }
]
}
你的任务是得到该用户所有文章的标题。现在开始!
fetchFromServer()
.then(JSON.parse)
.then(function(data){ return data.posts })
.then(function(posts){
return posts.map(function(post){ return post.title })
})
好吧,这不公平,是我催得太急了。(我还帮你写了上面这些代码 —— 你也许能更加优雅地解决这个问题,可能我已经跑题了)
由于承诺链( chains of promises )(也许你更喜欢称其为回调函数)基本都是与函数一起使用的,你无法简单直接地遍历数据,直到它先从服务器返回并被(无论视觉上或头脑中的)一团乱麻包裹住。
让我们再次出发,这回我们使用已经定义过的方法:
fetchFromServer()
.then(JSON.parse)
.then(get('posts'))
.then(map(get('title')))
Ok ,很少的逻辑,轻松地表达;如果没有柯里化函数我们是无法如此容易地做到的。
柯里化能给予你一种令人垂涎的表达能力。
我建议你立刻开始使用它。如果你已经熟稔于此,那么你一定会发现它的 API 接口直接好用。如果还没有,那么你应当与你的同事一起好好考虑一下了。
翻译自:Why Curry Helps
1
AngelCriss 2017-03-23 16:34:03 +08:00
以下文字来自 http://www.yinwang.org/blog-cn/2015/04/03/paradigms
函数式语言的“拥护者”们,往往认为这个世界本来应该是“纯”( pure )的,不应该有任何“副作用”。他们把一切的“赋值操作”看成低级弱智的作法。他们很在乎所谓尾递归,类型推导, fold , currying , maybe type 等等。他们以自己能写出使用这些特性的代码为豪。可是殊不知,那些东西其实除了能自我安慰,制造高人一等的幻觉,并不一定能带来真正优秀可靠的代码。 |
2
crashX 2017-03-23 16:44:55 +08:00
感觉没什么用,最新的 swift 都去掉这个特性了。
|
4
darluc OP @AngelCriss 说得很实在,请先欣赏外在的美
|
5
Arrowing 2017-03-23 16:48:15 +08:00
@AngelCriss 好难学,写的代码确实比较简洁,但是久了之后,回头来看,自己都看不懂。需要你十分、非常、绝对精通函数式编程,才能算是成功的。所以这个函数式编程推广得这么艰难,太难入门了。
|
6
FrankFang128 2017-03-23 17:11:54 +08:00
思维负担太重啦
|
7
alamaya 2017-03-23 17:20:38 +08:00
难道所谓简洁、优雅的代码就是写出来让别人看不懂?
|
8
Phariel 2017-03-23 17:25:08 +08:00 via Android
这东西要是被滥用也很美,你可能得全局搜索非常多次才能明白一个函数使用姿势如何,也得全局找很多次才能去更改源头。
|
9
Kilerd 2017-03-23 17:44:16 +08:00
关键字 python 偏函数 functools partial
|
10
QAPTEAWH 2017-03-23 17:49:59 +08:00
语法糖而已
核心是闭包 |
13
think2011 2017-03-23 18:21:29 +08:00
=> = > => => Orz
|
14
sagaxu 2017-03-23 21:52:48 +08:00
|
15
skydiver 2017-03-23 22:34:17 +08:00 via Android 1
柯里化和咖喱是一个词,所以原文说美味是双关,可以翻译过来没有这层意思了
|
17
longear 2017-03-24 00:12:05 +08:00 1
函数加里化(Currying)和偏函数应用(Partial Application)的比较
http://www.vaikan.com/currying-partial-application/ |