var add = curry(function (a,b,c) { return a + b + c; }); var add2 = add(2); var add5 = add2(3); alert(add5(5));
<script> function curry(f) { var length = f.length; if (length > 0) { return partial(f,length,[]); } else { return f; // f is already curried } } function partial(f,a) { return function () { var arity = length; var count = arguments.length; var args = new Array(count); var index = 0; while (index < count) { args[index] = arguments[index++]; } args = a.concat(args); return count < arity ? partial(f,arity - count,args) : f.apply(this,args); }; } </script>
但是,它不适用于以下情况:
// length :: [a] -> Number function length(a) { return a.length; } // filter :: (a -> Bool) -> [a] -> [a] var filter = curry(function (f,a) { return a.filter(f); }); // compose :: (b -> c) -> (a -> b) -> a -> c var compose = curry(function (f,g,x) { return f(g(x)); }); // countWhere :: (a -> Bool) -> [a] -> Number var countWhere = compose(compose(length),filter);
根据以下问题countWhere被定义为(length.).过滤:
What does (f .) . g mean in Haskell?
所以我应该可以使用countWhere如下:
countWhere(odd,[1,2,3,4,5]); function odd(n) { return n % 2 === 1; }
但是,不返回3(数组[1,5]的长度),它返回一个函数.我究竟做错了什么?
解决方法
我发布这是因为你分享了我对To “combine” functions in javascript in a functional way?的答案的评论我没有专门涵盖在那篇文章中的currying,因为它是一个非常有争议的话题,而不是真正的一个蠕虫病毒,我想打开那里.
当您似乎将自己的糖和便利添加到您的实施中时,我会谨慎使用“如何正确咖喱”的措辞.
无论如何,除此之外,我真的不打算这样做是一个辩论/辩护的职位.我想要能够在JavaScript中进行开放友好的讨论,同时强调我们的方法之间的一些差异.
无需再费周折…
澄清:
给定f是一个函数,f.length是n.咖喱(f)是g.我们用m个参数调用g.应该怎么办你说:
- If
m === 0
then just returng
.- If
m < n
then partially applyf
to them
new arguments,and return a new curried function which accepts the remainingn - m
arguments.- If
m === n
then applyf
to them
arguments. If the result is a function then curry the result. Finally,return the result.- If
m > n
then applyf
to the firstn
arguments. If the result is a function then curry the result. Finally,apply the result to the remainingm - n
arguments and return the new result.
让我们来看看@Aadit M Shah的代码实际做的代码示例
var add = curry(function(x,y) { return function(a,b) { return x + y + a + b; } }); var z = add(1,3); console.log(z(4)); // 10
这里有两件事情:
>你试图用可变参数支持调用curried函数.
>你自动调用返回的函数
我不相信在这里有很多的辩论空间,但是人们似乎错过了currying的实际
via: Wikipedia
In mathematics and computer science,currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions,each with a single argument…
我最大胆的是,因为它是如此重要;序列中的每个函数只需要一个参数;不像你所建议的那样(0,1或更多)参数.
你也可以在你的帖子中提到haskell,所以我假设你知道Haskell没有这样的东西,这个功能需要多个参数. (注意:一个需要元组的函数只是一个需要一个参数的函数,一个单独的元组).这样做的原因是深刻的,并且为您提供不具有可变参数的功能给您的表现力的灵活性.
所以让我们再问一下原来的问题:应该怎么办?
那么,每个函数只接受1个参数就很简单.在任何时候,如果提供超过1个参数,它们就被删除.
function id(x) { return x; }
当我们调用id(1,4)时会发生什么?当然我们只得到1回,4完全被忽视.这是:
> JavaScript如何工作
维基百科说维权应该如何工作
>我们应该如何实施我们自己的咖喱解决方案
Before we go further,I’m going to use ES6-style 07003 but I will also include the ES5 equivalent at the bottom of this post. (Probably later tonight.)
currying技术àla naomik
在这种方法中,我们编写一个咖喱功能,连续返回单参数函数,直到指定了所有参数为止
作为这个实现的结果,我们有6个多用途功能.
// no nonsense curry const curry = f => { const aux = (n,xs) => n === 0 ? f (...xs) : x => aux (n - 1,[...xs,x]) return aux (f.length,[]) } // demo let sum3 = curry(function(x,y,z) { return x + y + z; }); console.log (sum3 (3) (5) (-1)); // 7
好的,所以我们看到一个使用简单的辅助循环实现的咖喱技术.它没有依赖关系和一个低于5行代码的声明性定义.它允许功能部分应用,一次有1个参数,就像一个咖喱功能应该工作.
没有魔法,没有意想不到的自动调理,没有其他意外的后果.
但是呢,还有什么真正的意义呢?
那么,事实证明,我真的不是咖喱我写的功能.如下所示,我通常以咖喱形式定义所有可重用的功能.所以真的,你只需要咖喱,当你想与一些你没有控制的功能接口,也许来自一个lib或某些东西;其中一些可能有可变的界面!
我现在咖喱N
// the more versatile,curryN const curryN = n => f => { const aux = (n,x]) return aux (n,[]) }; // curry derived from curryN const curry = f => curryN (f.length) (f); // some caveman function let sumN = function() { return [].slice.call(arguments).reduce(function(a,b) { return a + b; }); }; // curry a fixed number of arguments let g = curryN (5) (sumN); console.log (g (1) (2) (3) (4) (5)); // 15
要咖喱还是不咖喱?就是那个问题
我们会写一些例子,我们的功能都是以咖喱的形式出现的.功能将保持极其简单.每个都有1个参数,每个都有一个返回表达式.
// composing two functions const comp = f => g => x => f (g (x)) const mod = y => x => x % y const eq = y => x => x === y const odd = comp (eq (1)) (mod (2)) console.log (odd(1)) // true console.log (odd(2)) // false
你的countWhere功能
// comp :: (b -> c) -> (a -> b) -> (a -> c) const comp = f => g => x => f(g(x)) // mod :: Int -> Int -> Int const mod = x => y => y % x // type Comparable = Number | String // eq :: Comparable -> Comparable -> Boolean const eq = x => y => y === x // odd :: Int -> Boolean const odd = comp (eq(1)) (mod(2)) // reduce :: (b -> a -> b) -> b -> ([a]) -> b const reduce = f => y => ([x,...xs]) => x === undefined ? y : reduce (f) (f(y)(x)) (xs) // filter :: (a -> Boolean) -> [a] -> [a] const filter = f => reduce (acc => x => f (x) ? [...acc,x] : acc) ([]) // length :: [a] -> Int const length = x => x.length // countWhere :: (a -> Boolean) -> [a] -> Int const countWhere = f => comp (length) (filter(f)); console.log (countWhere (odd) ([1,5])) // 3
备注
那么咖喱还是不咖喱?
// to curry const add3 = curry((a,c) => a + b + c ) // not to curry const add3 = a => b => c => a + b + c
使用ES6箭头功能成为当今JavaScripter的选择,我认为手动咖喱功能的选择是一个没有意义的选择.它实际上更短,只有以咖喱形式写出来的开销较少.
也就是说,您仍然要与不提供公开表现形式的libs进行接口.对于这种情况,我建议
咖喱咖喱N(上文定义)
>部分(如defined here)
@Iven,
你的咖喱N实现非常好本节仅供您使用.
const U = f=> f (f) const Y = U (h=> f=> f(x=> h (h) (f) (x))) const curryN = Y (h=> xs=> n=> f=> n === 0 ? f(...xs) : x=> h ([...xs,x]) (n-1) (f) ) ([]) const curry = f=> curryN (f.length) (f) const add3 = curry ((x,z)=> x + y + z) console .log (add3 (3) (6) (9))