类似的咖喱功能产生不同的结果

时间:2019-08-15 07:49:31

标签: javascript functional-programming currying

我正在学习函数式javascript,并且遇到了curry函数的两个不同实现。我试图了解两者之间的区别,它们看起来相似,但其中一种在某些情况下无法正确运行,而在其他情况下则可以正确运行。

我尝试过交换使用es6'const'定义的功能 适用于简单情况,但使用“过滤器”过滤字符串时,结果不正确,但使用整数可产生所需的结果。


//es6 
//Does not work well with filter when filtering strings
//but works correctly with numbers
const curry = (fn, initialArgs=[]) => (
    (...args) => (
        a => a.length === fn.length ? fn(...a) : curry(fn, a)
    )([...initialArgs, ...args])
);

//Regular js
//Works well for all cases
function curry(fn) {
  const arity = fn.length;

  return function $curry(...args) {
    if (args.length < arity) {
      return $curry.bind(null, ...args);
    }

    return fn.call(null, ...args);
  };
}

const match = curry((pattern, s) => s.match(pattern));
const filter = curry((f, xs) => xs.filter(f));

const hasQs = match(/q/i);

const filterWithQs = filter(hasQs);
console.log(filterWithQs(["hello", "quick", "sand", "qwerty", "quack"]));
//Output:
  //es6:
    [ 'hello', 'quick', 'sand', 'qwerty', 'quack' ]
 //regular:
    [ 'quick', 'qwerty', 'quack' ]

2 个答案:

答案 0 :(得分:1)

如果您将filter改为使用xs.filter(x => f(x))而不是xs.filter(f),它将起作用-

const filter = curry((f, xs) => xs.filter(x => f(x)))

// ...

console.log(filterWithQs(["hello", "quick", "sand", "qwerty", "quack"]))
// => [ 'quick', 'qwerty', 'quack' ]

这样做的原因是因为Array.prototype.filter三个(3)参数传递给“回调”功能,

  
      
  • callback-函数是谓词,用于测试数组的每个元素。返回true保留元素,否则返回false。它接受三个参数:      
        
    • element-数组中正在处理的当前元素。
    •   
    • index(可选)-数组中正在处理的当前元素的索引。
    •   
    • array(可选)-调用了数组过滤器。
    •   
  •   

您在f中使用的filtermatch(/q/i),因此当Array.prototype.filter调用它时,您会得到三(3)个额外的参数,而不是预期一(1)。在curry的上下文中,这意味着a.length将是四(4),并且由于4 === fn.lengthfalse(其中fn.length2 ),返回值为curry(fn, a),这是另一个函数。由于所有函数在JavaScript中都被视为 truthy 值,因此filter调用将返回所有输入字符串。

// your original code:
xs.filter(f)

// is equivalent to:
xs.filter((elem, index, arr) => f(elem, index, arr))

通过更改过滤器以使用...filter(x => f(x)),我们只允许将一(1)个参数传递给回调,因此curry将计算2 === 2,即{{1} },返回值是评估true的结果,它返回预期的match true

false

另一种可能更好的选择是将“ es6”中的// the updated code: xs.filter(x => f(x)) // is equivalent to: xs.filter((elem, index, arr) => f(elem)) 更改为=== >=-

curry

这使您可以“正常”“溢出”功能参数,而JavaScript则没有问题-

const curry = (fn, initialArgs=[]) => (
    (...args) => (
        a => a.length >= fn.length ? fn(...a) : curry(fn, a)
    )([...initialArgs, ...args])
)

// ...

console.log(filterWithQs(["hello", "quick", "sand", "qwerty", "quack"]))
// => [ 'quick', 'qwerty', 'quack' ]


最后,这是我过去写过const foo = (a, b, c) => // has only three (3) parameters console.log(a + b + c) foo(1,2,3,4,5) // called with five (5) args // still works // => 6的其他方式。我已经测试过它们每个都能为您的问题提供正确的输出-

通过辅助循环-

curry

多功能const curry = f => { const aux = (n, xs) => n === 0 ? f (...xs) : x => aux (n - 1, [...xs, x]) return aux (f.length, []) } ,可用于可变函数-

curryN

点差数-

const curryN = n => f => {
  const aux = (n, xs) =>
    n === 0 ? f (...xs) : x => aux (n - 1, [...xs, x])
  return aux (n, [])
};

// curry derived from curryN
const curry = f => curryN (f.length) (f)

the lambda calculus和Howard Curry的定点Y-combinator致敬-

const curry = (f, ...xs) => (...ys) =>
  f.length > xs.length + ys.length 
    ? curry (f, ...xs, ...ys)
    : f (...xs, ...ys)

和我的个人收藏-

const U =
  f => f (f)

const Y =
  U (h => f => f (x => U (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)

最后,@ Donat的答案很有趣,可以实现匿名递归-

// for binary (2-arity) functions
const curry2 = f => x => y => f (x, y)

// for ternary (3-arity) functions
const curry3 = f => x => y => z => f (x, y, z)

// for arbitrary arity
const partial = (f, ...xs) => (...ys) => f (...xs, ...ys)

答案 1 :(得分:0)

此处的主要区别不是es6语法,而是参数如何部分应用于函数。

第一版:curry(fn, a)

第二版:$curry.bind(null, ...args)

如果将第一个版本(es6)更改为fn.bind(null, ...args),则仅适用于一步一步(如示例所示)工作

es6语法中“ Regular js”版本的表示应如下所示(您需要在常量中为递归调用中的函数命名):

    curry = (fn) => {
        const c = (...args) => (
            args.length < fn.length ? c.bind(null, ...args) : fn(...args)
        );
        return c;
    }
相关问题