我正在学习函数式编程,我想知道是否有办法“组合”这样的函数:
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是一个函数,它将函数“组合”到自身中,如果不是,它将返回最终结果。 谢谢!
答案 0 :(得分:28)
这个概念来自可爱的数学。它被称为function composition。
f(x) = y
g(y) = z
g(f(x)) = z
(g•f)(x) = z
最后一行是" g的x等于z" 。 撰写函数的优点是消除 points 。请注意g(f(x)) = z
,我们接受x
输入并获得z
输出。这完全省略了中间y
。我们在这里说删除了点y 。
所以我们有一个撰写函数(g•f)(x) = z
,让它设置为h(x)
。
h(x) = (g•f)(x) = z
由于(g•f)
已经是一个函数,我们可以删除另一个点 x
h(x) = (g•f)(x) = z
h = (g•f)
函数组合是创建higher-order functions并使代码保持良好和干净的好方法。很容易理解我们为什么在您的Javascript中想要这个。
"让我们自己创作作品!"
好的,你听起来真的很兴奋。我们走了......function triple(x) {
return x * 3;
}
function plusOne(x) {
return x + 1;
}
function id(x) { return x; }
function compN(funcs) {
return funcs.reduce(function(g, f) {
return function(x) {
return g(f(x));
}
}, id);
}
var g = compN([triple, triple, plusOne]);
// g(x) = (triple • triple • plusOne)(x)
g(1);
//=> 18
评价
triple(triple(plusOne(1)))
triple(triple(2))
triple(6)
18
如果你对这个答案感到满意,那就好了。对于那些想要探索构建更好的高阶函数的人,请继续!
从其他函数中构建高阶函数的概念,我们可以重构和扩展我们对compN
的定义
首先,让我们了解compN
如何评估事物。我们假设我们要编写3个函数, a , b 和 c
// g(x) = a(b(c(x)));
// g(x) = (a•b•c)(x)
// g = (a•b•c)
var g = compN([a,b,c])
现在,compN
会在数组上调用reduce
并使用我们的id
函数对其进行初始化(原因显而易见)
reduce 的工作原理是它会在我们的数组中每个项目调用一次内部函数
function( g, f) { return function(x) { g(f(x)); }; }
我故意添加空格以使其与下面的表格对齐
iteration g f return explanation
----------------------------------------------------------------------------
#1 id a λx => g(f(x)) original return value
λx => id(a(x)) substitute for `g` and `f`
a' we'll call this "a prime"
#2 a' b λx => g(f(x)) original return value
λx => a'(b(x)) substitute for `g` and `f`
b' we'll call this "b prime"
#3 b' c λx => g(f(x)) original return value
λx => b'(c(x)) substitute for `g` and `f`
c' we'll call this "c prime"
>> c' is the final return value <<
因此,当我们致电compN([a,b,c])
时,c'
是返回给我们的函数。
&#34;当我们使用争论调用该函数时会发生什么,例如c'(5)
?&#34;
alias original x return
----------------------------------------------------------------------------
c'(5); λx => b'(c(x)) 5 b'(c(5))
b'(c(5)) λx => a'(b(x)) c(5) a'(b(c(5)))
a'(b(c(5))) λx => id(a(x)) b(c(5)) id(a(b(c(5)))) <-- final return value
换句话说......
compN([a,b,c])(5) === id(a(b(c(5))));
&#34;那甜言蜜语,但这让我的大脑难以理解。&#34;
好的,我同意,如果你像我一样,当我在3个月后回到这个代码时,我会想知道它到底是做什么的。
所以要开始改进compN
,请让我们先修复
// g(x) = a(b(c(x)));
// g(x) = (a•b•c)(x)
// g = (a•b•c)
var g = compN([a,b,c])
如果我们看另一个例子,也许我们会得到一个提示
[1,2,3].reduce(function(x,y) { return x + y; }, 0);
//=> 6
与
相同((0 + 1) + 2) + 3
//=> 6
隐藏在reduce
中间的是此功能
function (x,y) { return x + y; }
看到了吗?这看起来像一个非常基本的功能,不是吗?并且可以重复使用!如果您认为add
是一个好名字,那么你是对的!让我们看一下再次减少
function add(x,y) { return x + y; }
[1,2,3].reduce(add, 0);
//=> 6
那个 super 也很容易理解。我可以随时回到那段代码,并且知道完全正在进行什么。
我知道你在想什么
1 + 2 + 3
看起来非常像
a • b • c
&#34;也许如果我们从compN中提取reduce迭代器,我们可以简化compN的定义......&#34;
这是我们原来的compN
// original
function compN(fs) {
return fs.reduce(function(g, f) {
return function(x) {
return g(f(x));
}
}, id);
}
让我们取出迭代器并将其称为comp
function comp(g, f) {
return function(x) {
return g(f(x));
}
}
var g = comp(triple)(plusOne); // λx => triple(plusOne(x))
g(1); // triple(plusOne(1))
//=> 6
好的,让我们看看修改后的compN
现在
// revision 1
function compN(fs) {
return fs.reduce(comp, id);
}
&#34; Gnarly改进!那么我们现在全部完成了吗?&#34;
哈哈哈哈,没有。看看reduce
只是坐在那里。这是一个非常有用的功能,我很确定我们可以在很多地方使用它
function reduce(f, i) {
return function(xs) {
return xs.reduce(f, i);
}
}
reduce(add, 0)([1,2,3]); //=> 6
reduce(comp, id)([a, b, c]); //=> λx => id(a(b(c(x))))
最后一行应该是我们compN
函数
// revision 2
function compN(fs) {
return reduce(comp, id)(fs);
}
&#34;看起来并不比修订版1好......&#34;
是的,我知道!但是你肯定会在每一行的末尾看到悬空(fs)
,对吗?
你不会写这个,对吧?
// useless wrapper
function max(x) {
return Math.max(x);
}
max(3,1);
//=> 3
咄!这与
相同var max = Math.max;
max(3,1);
//=> 3
所以我提出最后的决定......
// recap
function reduce(f, i) {
return function(xs) {
return xs.reduce(f, i);
};
}
function id(x) { return x; }
function comp(g, f) {
return function(x) {
return g(f(x));
};
}
// revision 3
var compN = reduce(comp, id);
&#34;这仍然以同样的方式运作?&#34;
哎呀,确实如此!
function triple(x) {
return x * 3;
}
function plusOne(x) {
return x + 1;
}
var g = compN([triple, triple, plusOne]); // λx => id(triple(triple(plusOne(x))))
g(1); // id(triple(triple(plusOne(1))))
//=> 18
&#34;但为什么这样更好?&#34;
嗯,这很简单。当然我们有更多代码,但我们现在有 4 可重复使用的功能,而不仅仅是 1 。每个功能都有一个简单的任务,很容易立即识别。与原始函数相比,我们最终得到的代码比命令式更多声明性。这在下面进一步强调......
现在真的很酷。我不会在这里潜水太深,但 ES6 让我们感到惊讶。
// identical functionality as above
let id = x => x;
let reduce = (f,i) => xs => xs.reduce(f,i);
let comp = (g,f) => x => g(f(x));
let compN = reduce(comp, id);
// your functions
let triple = x => x * 3;
let plusOne = x => x + 1;
// try it out!
let g = compN([triple, triple, plusOne]);
console.log(g(1));
//=> 18
继续并将其粘贴到Babel REPL以查看是否有效
这就是全部,伙计们!
答案 1 :(得分:1)
其他答案中已经详细介绍了函数组成,主要是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),
);
答案 2 :(得分:0)
您只需在返回值本身上调用该函数,例如:
plusOne(triple(triple(1))) // 10
isZero(triple(plusOne(-1))) // true
答案 3 :(得分:0)
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)
答案 4 :(得分:0)
也可以通过在 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));