Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
为什么要柯里化(why-curry-helps)slide http://git.io/why-curry-helps 📈

还记得 Haskell Curry吗,

多巧啊, 人家姓 Curry 名 Haskell, 难怪 Haskell 语言会自动柯里化, 呵呵. 但是不奇怪吗, 为什么要柯里化呢. 为什么如此重要导致 Haskell 会默认自动柯里化所有函数, 不就是返回一个部分配置好的函数吗.

我们来看一个 Haskell 的代码.

max 3 4
(max 3) 4

结果都是4, 这有设么用呢.

这里看不出来, 放到高阶函数试试. 什么? 看不懂天书 Haskell, 来看看 JavaScript 吧.

我们来解一个问题

1. 写一个函数, 可以连接字符数组, 如 f(['1','2']) => '12'

好吧,如果不用柯里化, 怎么写? 啊哈 reduce

var concatArray = function(chars){
  return chars.reduce(function(a, b){
  	return a.concat(b);
  });
}
concat(['1','2','3']) // => '123'

很简单,对吧.

2. 现在我要其中所有数字加1, 然后在连接

var concatArray = function(chars, inc){
  return chars.map(function(char){
  	return (+char)+inc + '';
  }).reduce(function(a,b){
      return a.concat(b)
  });
}
console.log(concatArray(['1','2','3'], 1))// => '234'

3. 所有数字乘以2, 再重构试试看

var multiple = function(a, b){
  return +a*b + ''
}
var concatArray = function(chars, inc){
  return chars.map(function(char){
  	return multiple(char, inc);
  }).reduce(function(a,b){
      return a.concat(b)
  });
}
console.log(concatArray(['1','2','3'], 2)) // => '246'

是不是已经看出问题了呢? 如果我在需要每个数字都减2,是不是很麻烦呢.需要将map 参数匿名函数中的 multiple 函数换掉. 这样一来concatArray就不能同时处理加, 乘和减? 那么怎么能把他提取出来呢? 来对比下柯里化的解法.

柯里化函数接口

var multiple = function(a){
  return function(b){
    return +b*a + ''
  }
}

var plus = function(a){
  return function(b){
    return (+b)+a + ''
  }
}
var concatArray = function(chars, stylishChar){
  return chars.map(stylishChar)
    .reduce(function(a,b){
      return a.concat(b)
  });
}
console.log(concatArray(['1','2','3'], multiple(2)))
console.log(concatArray(['1','2','3'], plus(2)))

有什么不一样呢

  1. 处理数组中字符的函数被提取出来, 作为参数传入
  2. 提取成柯里化的函数, 部分配置好后传入, 好处显而易见, 这下接口非常通畅 无论是外层调用
concatArray(['1','2','3'], multiple(2))

还是内部的 map 函数

chars.map(stylishChar)

这些接口都清晰了很多, 不是吗

这就是函数式的思想, 用已有的函数组合出新的函数, 而柯里化每消费一个参数, 都会返回一个新的部分配置的函数, 这为函数组合提供了更灵活的手段, 并且使得接口更为流畅.

再加上自动柯里化的库 ramda, 简直就完美了

var multiple = ramda.curry(function(a, b){
  return +b*a + ''
})
var plus = ramda.curry(function(a, b){
  return (+b)+a + ''
})

JS Bin<script src="http://static.jsbin.com/js/embed.js"></script>

Analytics

@dandananddada

This comment has been minimized.

Copy link

commented Oct 20, 2015

66666666

@jackhutu

This comment has been minimized.

Copy link

commented Nov 30, 2015

concat(['1','2','3']) // => '123' 应该改为 concatArray(['1','2','3']) // => '123'

@zjien

This comment has been minimized.

Copy link

commented Jan 30, 2016

噢,柯里化就是把函数的一部分再提取出来,然后把这一部分配置好再作为一个函数返回给上一层函数,我的理解对吗?

@Wangbaogang

This comment has been minimized.

Copy link

commented Feb 29, 2016

恩恩,学习了

@flftfqwxf

This comment has been minimized.

Copy link

commented Jul 9, 2016

但是 柯里化,如果是自己的代码还好,如果是看别人的代码,什么时候返回什么函数,在复杂的逻辑里,很容易搞晕,感觉不是特别环境下,用处没有宣传的那么大,反而造成认知成本

@ghost

This comment has been minimized.

Copy link

commented Mar 25, 2017

concatArray不能称作科里化的函数吧,因为你只传一个参数过去之后并不能返回一个函数。我觉得concatArray只是普通的高阶函数而已。

@linjinxing

This comment has been minimized.

Copy link

commented Apr 4, 2017

mark

@libchaos

This comment has been minimized.

Copy link

commented Sep 11, 2017

@tomatoo you are right

@zhangfaliang

This comment has been minimized.

Copy link

commented Sep 11, 2017

学习了,柯里化和高阶有什么区别

@Liang3p

This comment has been minimized.

Copy link

commented Jan 11, 2019

学习了,slide比文章更容易懂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.