【问题标题】:How to compose functions of varying arity using Lodash flow?如何使用 Lodash 流组合不同数量的函数?
【发布时间】:2017-02-10 14:38:31
【问题描述】:

我想做一些函数组合。我已经知道了:

如果f3(x) 应与f1(f2(x)) 相同 然后f3 = _.flowRight(f1,f2);

如果f3(x,y) 应与f1(x, f2(y)) 相同 那么……?

(用例是node.js/express中间件函数的组合。)

【问题讨论】:

  • 我昨天写了一个关于 lodash 流程的答案。我认为您会发现它非常有用,因为某些功能不是要组合的:stackoverflow.com/a/42139851/633183
  • 我在一小时前写了另一个关于在现有代码库中组合的答案。我想你会发现它与这个主题相关:stackoverflow.com/a/42164779/633183
  • 你的“问题”是你试图组合各种 arity 的函数;一元函数与二元函数。当专门使用一元函数时,函数组合效果最好。简短的回答是const f3 = (x,y) => f1(x, f2(y))——让简单变得简单。
  • @naomik 感谢您的回答,并且您对元数(一元或二元,具有一个、两个甚至更多参数的函数)是正确的。就我而言,我可以争辩说,我所有的中间件函数都是二进制的,所以有两个参数:过滤器值和处理程序。所以我很确定,它对我的​​用例来说是“通用的”。

标签: javascript functional-programming lodash composition


【解决方案1】:

在下图中,我使用{_} 作为值的占位符。把它想象成代码中的一个,我们在其中传递一些东西。

好吧,让我们想象一下你的函数必须做什么......

  • 这看起来像是通用转换吗?即,您认为我们可以在很多地方使用它吗? – 函数式编程促进构建高度可重用且可以以各种方式组合的函数。
  • f1f2 有什么区别? f1 是一元函数,只能得到一个 arg,f2 是一个二元函数,只能得到两个。你会记住哪个去哪个地方吗?
  • 是什么决定了f1(x)f2 中的位置?
    • 比较f2(y,f1(x)) ...
    • f2(f1(x),y)
    • 其中一个比另一个更有用吗?
    • 你会记住f1 获得的位置吗?

回想一下,函数组合应该能够将任意数量的函数链接在一起。为了帮助您了解someFunc 的无用性,让我们假设它最多接受 3 个函数和 3 个参数。

  • 这里甚至有模式吗?也许吧,但你仍然有尴尬的一元函数f1,它只得到一个 arg,而 f2f3 各得到 2 个
  • f2f3 是否真的需要右侧前面函数调用的值总是
    • 比较f3(z,f2(y,f1(x)))
    • f3(f2(y,f1(x)),z)
    • 也许f3 需要向左连接,但f2 从右侧连接?
    • 我无法想象你的整个二进制函数 API 会神奇地需要在同一个地方链接参数
  • 您已经在合成中混合了一元函数和二元函数;那为什么还要武断地把它限制在那些类型的函数上呢?具有 3 个或更多参数的函数呢?

答案是自我实现

函数组合在这里被滥用。函数组合几乎只在你组合一元函数时才有效(每个函数接受 1 个参数)。当与更高元的函数混合时,它会立即崩溃并且无法泛化。

现在回到你的代码,如果f3需要一个名字,它是f1f2和两个参数的组合,它应该简单地表示为……

const f3 = (x,y) => f1(x, f2(y))

因为它做出了如此多的任意选择,它不能以任何有用的方式概括。就这样吧。


“那么有什么方法可以组合不同数量的函数吗?”

当然,有几种实用的技术。我将在此处演示高度实用的partial 函数的使用

const partial = (f,...xs) => (...ys) => f(...xs, ...ys)

const add = (x,y) => x + y

const mult = (x,y) => x * y

const sq = x => mult (x,x)

// R.I.P. lodash.flowRight
const compose = ([f,...fs]) => x =>
  f === undefined ? x : f (compose (fs) (x))
                  
let f = compose([partial(add, 1), sq, partial(mult, 3)])

console.log(f(2))
// add(1, square(mult(3, 2)))
// add(1, square(6))
// add(1, 36)
// => 37

哦,顺便说一句,我们用一行代码替换了 Lodash 的 flowRight(复杂 flow 的包装器)。

【讨论】:

  • 它应该能够组合具有完全相同的arity的函数
  • 你对二进制函数有什么建议?三元函数? N元函数?
  • 具有与参数长度匹配的 N 长度的数组输出
  • 您无需更改 compose 即可执行此操作 - 只需调用 const apply = f => (xs = []) => f (...xs)
【解决方案2】:

听起来您有一个非常具体的要求,可能没有 lodash 等效项。

为什么不为此编写自己的辅助函数?

function composeFuncs(f1, f2) {
  return function(x, y) {
    return f1.call(this, x, f2.call(this, y));
  };
}

var myObj = {
  add: function(val1, val2) {
    return this.myVal + val1 + val2
  },
  mult: function(val) {
    return this.myVal * val
  },
  myVal: 7
};

myObj.newFunc = composeFuncs(myObj.add, myObj.mult);

// 7 + 1 + 7 * 2 = 22
console.log(myObj.newFunc(1, 2));

编辑:已更新为处理 this 的方式与 _.flowRight 的处理方式相同。

【讨论】:

  • 当然我可以这样做,但是我可能必须像 lodash 中的 _.flowRight 那样处理“this”。由于我对以函数式编程方式做很多事情还很陌生,我想,可能有一个众所周知的模式来处理我需要的东西。 :)
  • @matths 修改为包含this 传播。你能举例说明你想在哪里使用它吗?我不熟悉一个众所周知的模式,但可能有一个。
  • 我目前正在编写 express/connect 之类的中间件函数,例如一个是 method(m, handler),其中 m 可以是 get、post、put 等,另一个可能是 path(p, handler)。组合应该是request(m, p, handler),它运行method(m, path(p, handler))。每个中间件返回值本身就是一个处理函数。我不确定,我可以很好地解释。
  • @matths 一般来说,函数式编程在很大程度上不关心上下文this - JS 是多范式,this 主要适合以 OOP 风格编写程序
猜你喜欢
  • 2017-06-27
  • 1970-01-01
  • 1970-01-01
  • 2020-06-14
  • 2016-09-15
  • 1970-01-01
  • 2017-09-20
  • 1970-01-01
  • 2020-09-16
相关资源
最近更新 更多