【问题标题】:To "combine" functions in javascript in a functional way?以功能的方式“组合”javascript中的函数?
【发布时间】:2021-01-02 23:55:55
【问题描述】:

我正在学习函数式编程,我想知道是否有一种方法可以像这样“组合”函数:

function triple(x) {
    return x * 3;
}
function plusOne(x) {
    return x + 1;
}
function isZero(x) {
    return x === 0;
}
combine(1); //1
combine(triple)(triple)(plusOne)(1); // 10
combine(plusOne)(triple)(isZero)(-1); // true

如果 para 是一个函数,它会将函数“组合”到自身中,如果不是,它将返回最终结果。 谢谢!

【问题讨论】:

  • @u_mulder 参数我猜
  • 在“组合”功能时问题不清楚
  • 以下代码有效,因为它返回一个函数...x = function(){return function(b){return b*2}}; x()(2) == 4 ...虽然不是你想要的

标签: javascript functional-programming


【解决方案1】:

遗产

这是数学中的一个概念,称为function composition

       f(x) = y
       g(y) = z

    g(f(x)) = z

   (g•f)(x) = z

最后一行是 "g of f of x equals z"。组合函数的优点在于消除了。请注意,在 g(f(x)) = z 中,我们采用 x 输入并获得 z 输出。这会跳过中间点y

Composition 是创建 higher-order functions 并保持代码干净整洁的好方法。很明显我们为什么要在 Javascript 程序中使用它。


补偿

JavaScript 是一种多范式语言,对函数有丰富的支持。我们可以创建一个简单的comp 函数,它结合了两个输入函数gf,并产生一个new 函数-

function triple(x) {
  return x * 3
}

function plusOne(x) {
  return x + 1
}

function comp(g, f) {
  return function(x) {
    return g(f(x))        // "g of f of x"
  }
}

const myfunc =
  comp(triple, plusOne)

console.log(myfunc(1))

评估

triple(plusOne(1))
triple(2)
6

撰写

正如问题所暗示的那样,我们很可能想要组合两个以上的功能。下面我们编写compose,它使用输入函数的allreduces 使用我们上面的简单comp。如果没有给出函数,我们返回空函数,identity -

const triple = (x) =>
  x * 3

const plusOne = (x) =>
  x + 1

const comp = (g, f) =>
  x => g(f(x))                     // "g of f of x"

const identity = (x) =>
  x

const compose = (...all) =>
  all.reduce(comp, identity)

const myfunc =
  compose(triple, triple, plusOne) // any amount of funcs

console.log(myfunc(1))

评估

triple(triple(plusOne(1)))
triple(triple(2))
triple(6)
18

管道

您可以随心所欲地发挥创意。下面,我们写pipe,它允许我们的程序以舒适的从左到右的方向阅读-

const triple = (x) =>
  x * 3

const plusOne = (x) =>
  x + 1

const pipe = x =>
  f => pipe(f(x))

pipe(1)(plusOne)(triple)(triple)(console.log)           // 18
pipe(3)(triple)(plusOne)(triple)(plusOne)(console.log)  // 31

表达式一的评估 -

f => pipe(f(1))
pipe(plusOne(1))
f => pipe(f(2))
pipe(triple(2))
f => pipe(f(6))
pipe(triple(6))
f => pipe(f(18))
pipe(console.log(18))
18

和表达式二-

f => pipe(f(3))
pipe(triple(3))
f => pipe(f(9))
pipe(plusOne(9))
f => pipe(f(10))
pipe(triple(10))
f => pipe(f(30))
pipe(plusOne(31))
f => pipe(f(31))
pipe(console.log(31))
31

相关技术

柯里化函数部分应用是与函数组合相结合的概念。上面的pipeanother Q&A中介绍为$,在这里再次演示-

const $ = x =>           // "pipe", or whatever name you pick
  k => $ (k (x))
  
const add = x => y =>    // curried add
  x + y

const mult = x => y =>   // curried mult
  x * y
  
$ (1)                    // 1
  (add (2))              // + 2 = 3
  (mult (6))             // * 6 = 18
  (console.log)          // 18
  
$ (7)                    // 7
  (add (1))              // + 1 = 8
  (mult (8))             // * 8 = 64
  (mult (2))             // * 2 = 128
  (mult (2))             // * 2 = 256
  (console.log)          // 256

【讨论】:

  • 太棒了!我花了很长时间阅读它,它对我有很大帮助。感谢您的出色工作!
  • @user3016883 我发现了几个错别字并修正了它们。如果您遇到麻烦,希望编辑对您有所帮助^.^
  • 很好的答案,但它完全忽略了这样一个事实,即只要第一个参数是一个函数,那么它就应该无限期地咖喱:stackoverflow.com/a/65537611/4099454。而您的版本似乎根本没有咖喱
  • Hitmands,我一直想修改这个答案已经有一段时间了。感谢您尊敬的评论,我终于解决了:D
【解决方案2】:
function triple(x) {
    return x * 3;
}
function plusOne(x) {
    return x + 1;
}
function isZero(x) {
    return x === 0;
}

var combine = function (v) {
    var fn = [];
    function _f(v) {
        if (typeof v === 'function') {
            fn.push(v);
            return _f;
        } else {
            return fn.reduce(function (x, f) { return f(x); }, v);
        }
    }
    return _f(v);
};

var a, b;
console.log(combine(1)); //1
console.log(combine(triple)(triple)(plusOne)(1)); // 10
console.log(combine(plusOne)(triple)(isZero)(-1)); // true
console.log(a = combine(plusOne)); // function ...
console.log(b = a(triple)); // function ...
console.log(b(5)); // 18
console.log(combine(triple)(plusOne)(triple)(plusOne)(triple)(plusOne)(1)); // 40
// @naomik's examples
var f = combine(triple); 
var g = combine(triple)(triple); 
console.log(f(1)); // 3
console.log(g(1)); // 9 (not 6 as you stated)

【讨论】:

  • 制作两个函数:var f = combine(triple); var g = combine(triple)(triple); 调用f(1); // 27,然后调用g(1) // 1。显然f(1) // should be 3g(1) // should be 6。这失败了,因为您依赖用户在创建新链之前完成链。问题是combine.fn 是有状态的,随后对combine 的调用将导致其他函数的fn 状态发生突变。
  • 我知道这个问题,但是你对combine(triple)(triple); 有什么期望?调用总是以一个值结束,所以规则是 function* value (bfn)。
  • 我希望 combine(triple)(triple) 为我创建一个新函数,我可以将其分配给变量、稍后调用或作为值传递给另一个函数。正如我在上面用fg 演示的那样。
  • 我已经删除了我的反对票,因为您提供了一个实际有效的答案。如果坚持使用这种类型的接口,请参阅this gist 以获得更多功能的实现。我在那里附上了一些其他的笔记。 ^.^
  • 我没有进入界面,因此我将其更改为只有一个初始函数和返回函数。
【解决方案3】:

其他答案中已经详细说明了功能组成,主要是https://stackoverflow.com/a/30198265/4099454,所以我的2美分直接回答您的最新问题:

如果 para 是一个函数,它会将函数“组合”到自身中,如果不是,它将返回最终结果。谢谢!

const chain = (g, f = x => x) => 
  typeof g === 'function'
  ? (y) => chain(y, (x) => g(f(x)))
  : f(g);

// ====

const triple = x => x * 3;
const inc = x => x + 1;
const isZero = x => x === 0;

console.log(
  chain(inc)(triple)(isZero)(-1),
);

【讨论】:

  • 与 OP 的确切提案相匹配的出色工作。当心类型检查输入并赋予函数特殊行为意味着chain 不能再接受函数作为参数。例如,我们经常需要计算一个函数,例如一个 thunk 或一个延续,并将其作为参数传递给另一个函数。在此设置中,chain 将危险地组合函数,而不是将其作为参数传递给组合。
【解决方案4】:

还可以通过在 JavaScript 中组合简单函数来构建复杂的功能。从某种意义上说,组合就是函数的嵌套,将一个输入的结果作为输入传递给下一个。但是,我们将创建一个高阶函数 compose(),而不是创建一个难以理解的嵌套量,它接受我们想要组合的所有函数,并返回一个新函数以在我们的应用程序中使用。

function triple(x) {
  return x * 3;
}
function plusOne(x) {
  return x + 1;
}
function isZero(x) {
  return x === 0;
}

const compose = (...fns) => x =>
  fns.reduce((acc, cur) => {
    return cur(acc);
  }, x);

const withCompose = compose(triple, triple, isZero);
console.log(withCompose(1));

【讨论】:

  • 撰写应该是从右到左,你写的是一个管道
【解决方案5】:

您可以简单地调用函数本身的返回值,例如:

plusOne(triple(triple(1))) // 10
isZero(triple(plusOne(-1))) // true

【讨论】:

  • 你能解释一下为什么我的回答如此糟糕吗?
  • 我没有否决您的帖子,但它并没有真正回答问题。 OP 希望以编程方式构建功能的“链”或“管道”。它被称为 function composition 并且 javascript 没有内置函数。您的回答可能会告诉 OP 一些他/她已经知道该怎么做的事情。
  • @naomik 我会说这取决于 OP 作为“组合”的含义,如果 OP 已经知道该解决方案,我想他们会对此发表评论。我只是说您可以通过执行f(g(x)) 而不是创建一个F = f(g(x)) 的函数来获得正确的结果,假设OP 可能是初学者并且可能不知道这一点。它确实(在某种程度上)结合了这些功能,并且确实产生了 OP 想要的正确结果,所以我不明白这不是一个答案。
  • @naomik 虽然我可能只是误解了这个问题,但似乎很清楚你的答案就是 OP 所要寻找的。​​span>
猜你喜欢
  • 2020-04-20
  • 2016-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-23
  • 2015-03-02
  • 1970-01-01
相关资源
最近更新 更多