为什么柯里化有用(下午茶译文赏析) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
darluc
V2EX    Javascript

为什么柯里化有用(下午茶译文赏析)

  •  1
     
  •   darluc 2017-03-23 16:07:40 +08:00 4661 次点击
    这是一个创建于 3201 天前的主题,其中的信息可能已经有所发展或是发生改变。

    查看原文点击这里

    写一段可以无限被重用的代码,对于程序员来说无异于黄粱美梦。代码表达清晰是因为代码要表达需求,代码易于重用……好吧,只是因为你要重复使用它。两者不可得兼,你还能奢求更多么?

    柯里化可以帮上忙。

    什么是柯里化,为何它如此美味?

    通常, 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

    查看原文点击这里

    17 条回复    2017-03-24 00:12:05 +08:00
    AngelCriss
        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 等等。他们以自己能写出使用这些特性的代码为豪。可是殊不知,那些东西其实除了能自我安慰,制造高人一等的幻觉,并不一定能带来真正优秀可靠的代码。
    crashX
        2
    crashX  
       2017-03-23 16:44:55 +08:00
    感觉没什么用,最新的 swift 都去掉这个特性了。
    darluc
        3
    darluc  
    OP
       2017-03-23 16:46:51 +08:00
    @crashX 看起来挺美,尤其是异步嵌套的时候
    darluc
        4
    darluc  
    OP
       2017-03-23 16:47:32 +08:00
    @AngelCriss 说得很实在,请先欣赏外在的美
    Arrowing
        5
    Arrowing  
       2017-03-23 16:48:15 +08:00
    @AngelCriss 好难学,写的代码确实比较简洁,但是久了之后,回头来看,自己都看不懂。需要你十分、非常、绝对精通函数式编程,才能算是成功的。所以这个函数式编程推广得这么艰难,太难入门了。
    FrankFang128
        6
    FrankFang128  
       2017-03-23 17:11:54 +08:00
    思维负担太重啦
    alamaya
        7
    alamaya  
       2017-03-23 17:20:38 +08:00
    难道所谓简洁、优雅的代码就是写出来让别人看不懂?
    Phariel
        8
    Phariel  
       2017-03-23 17:25:08 +08:00 via Android
    这东西要是被滥用也很美,你可能得全局搜索非常多次才能明白一个函数使用姿势如何,也得全局找很多次才能去更改源头。
    Kilerd
        9
    Kilerd  
       2017-03-23 17:44:16 +08:00
    关键字 python 偏函数 functools partial
    QAPTEAWH
        10
    QAPTEAWH  
       2017-03-23 17:49:59 +08:00
    语法糖而已

    核心是闭包
    Swift3030
        11
    Swift3030  
       2017-03-23 18:00:57 +08:00
    @crashX 只是去掉语法糖, curried function 还是有的。
    darluc
        12
    darluc  
    OP
       2017-03-23 18:15:59 +08:00
    @Kilerd 「偏函数」受教!
    think2011
        13
    think2011  
       2017-03-23 18:21:29 +08:00
    => = > => => Orz
    skydiver
        15
    skydiver  
       2017-03-23 22:34:17 +08:00 via Android   1
    柯里化和咖喱是一个词,所以原文说美味是双关,可以翻译过来没有这层意思了
    skydiver
        16
    skydiver  
       2017-03-23 22:34:48 +08:00 via Android
    @skydiver 可以=可惜
    longear
        17
    longear  
       2017-03-24 00:12:05 +08:00   1
    函数加里化(Currying)和偏函数应用(Partial Application)的比较
    http://www.vaikan.com/currying-partial-application/
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2341 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 33ms UTC 01:45 PVG 09:45 LAX 17:45 JFK 20:45
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86