在函数数组上使用reduce方法时,我很难跟踪reduce对数组的影响。
comboFunc(num, functionsArr) {
return functionsArr.reduce(function (last, current) {
return current(last);
}, input);
}
所以使用functionsArr = [add, multi]
并且函数add
和multi
为
function add(num) {
return num + 1;
}
和
function multi(num) {
return num * 30;
}
函数comboFunc
将:
comboFunc(2, functionsArr); //returns 90;
通过将函数应用于数组的每个元素,我理解reduce方法如何处理数组数组,将数组折叠成一个值。这个示例使用comboFunc将函数数组作为参数使用了足够简单的数学运算,我可以理解reduce如何以数学方式生成90的值,但我不理解逐行和从函数级别每次迭代发生的事情。我无法在脑海中清楚地表达出究竟发生了什么 - 如何将2的输入从函数传递到函数以及关闭每个函数的顺序触发等等。
如果reduce方法应用你传入数组的每个元素的函数,并且当数组中的每个元素都是一个函数时...我怎么能写出来自己理解它的外观呢?
传递给reduce方法的函数返回current(last)
,这是一个返回函数的函数。那么这会使last
等于add
函数而current
等于multi
?换句话说,返回multi(add)
?如果我的逻辑是正确的,这意味着发生的事情是输入然后被传递到add
并计算,然后返回的值被传递到multi
并计算最终返回90?
答案 0 :(得分:2)
看起来您正在尝试创建一个composition函数,该函数将一系列函数应用于输入, num
让我们首先介绍一些语法错误。首先,您错过了function
关键字
// your code
comboFunc(num, functionsArr) {
// should be
function comboFunc(num, functionsArr) {
接下来,您已将参数命名为num
,但之后在函数体中将其引用为input
// your code
}, input);
// should be
}, num);
因此,在您解决此问题后,您的功能会起作用,但如果我们以稍微不同的方式处理问题该怎么办?
免责声明:此答案并未指导您完成所提供的功能。相反,它通过证明
comboFunc
以过于复杂的方式编写而同情你的困难。反过来,我给你一个函数的替代定义,它的操作相同,但是 lot 更容易阅读/理解。
首先,我认为我们应该回到函数组合的基础知识。
我喜欢将功能构图描绘成一张小地图。
f(x)=y g(y)=z
+--------> y -------+
| |
| |
| v
x ----------------> z
?
在上面的示例中,我们可以使用您的add
函数轻松替换 f ,使用multi
函数轻松替换 g 。现在定义?也应该是相当明显的。
add(2) multi(3)
+--------> 3 -------+
| |
| |
| v
2 ----------------> 90
h(x) = g(f(x)) = z
h(x) = (g∘f)(x) = z
h(2) = 90
(g∘f)可以说是“ g f ”,它只是意味着应用 x 首先到 f ,然后将该结果应用于 g 。
注意 y 甚至不存在。我们根据 x 定义了 h ,它为我们提供了直接“地图”到 z ,跳过 y < / em>完全。
将两个函数组合在一起是一个非常基本的操作,我们可以定义一个非常容易实现的函数
function comp(g,f) {
return function(x) {
return g(f(x));
}
}
var h = comp(multi,add);
h(2); // 90
// h(2) = multi(add(2)) = 90
“是的,但我正在尝试使用N个功能。不仅仅是2个。”
好的,comp
适用于两个功能,但我已经简化了问题。如果我们想要组合3,4或甚至更多功能,你可能想知道这是如何工作的。
让我们再看一遍
a(w) = x
b(x) = y
c(y) = z
d(w) = c(b(a(w))) = z
d(w) = (c∘b∘a)(w) = z
仔细查看∘
。我们知道这是我们的comp
函数,我们可以看到它出现在每个函数之间, a , b , c
在我们走得更远之前,假设我们有一组数字
var xs = [1,2,3];
如果我们想要对数字求和,我们需要调用
var sum = 1 + 2 + 3
查看每个号码之间+
的显示方式?通过我们的一系列功能,它没有什么不同!
var fs = [c,b,a];
var d = (c ∘ b ∘ a)
我们只需要制作有效的JavaScript。它也很容易,因为我们已经将∘
定义为comp
。
在列表中的术语之间调用函数称为线性fold,JavaScript具有执行此操作的函数reduce
。
好吧,让我们用吧!我们将使用comp
函数
function compN(fs) {
return fs.reduce(comp);
}
var h = compN([multi, add]);
h(2); // 90
// h(2) = multi(add(2)) = 90
// h(2) = (multi ∘ add)(2) = 90
好的,所以它适用于我们原来的两个功能,但让我们确保它适用于更长的列表
var i = compN([multi, add, add, add]);
i(2); // 150
// i(2) = multi(add(add(add(2))) = 150
// i(2) = (multi ∘ add ∘ add ∘ add)(2) = 150
好的,让我们回顾一下
function add(num) {
return num + 1;
}
function multi(num) {
return num * 30;
}
function comp(g,f) {
return function(x) {
return g(f(x));
}
}
function compN(fs) {
return fs.reduce(comp);
}
compN([multi, add])(2); // 90
“但为什么这样更好?”
正如我们在y
中删除h(x) = (g∘f)(x) = z
一样,请注意我们的compN
函数与num
,last
或{{ 1}}。我们已经放弃了3个变量供我们的大脑思考,而且我们的功能正在做得更清楚。
current
使用 f 和 g 并将它们组合在一起制作comp
。
g ∘ f
在功能列表中的每个字词之间调用compN
对我而言,这是非常直接且易于理解的。只需查看每个comp
和comp
的函数体,我就可以立即识别出意图。
将其与
进行比较
compN
难怪你难以跟上它。 comboFunc(num, functionsArr) {
return functionsArr.reduce(function (last, current) {
return current(last);
}, input);
}
从一开始就过多地关注自己,因为它试图成为两个操作而不是一个操作。
所以,我知道我并没有引导你完成你的原始功能,但我希望在阅读完这个答案之后,你会对功能构成有更深刻的理解,并建立自己独立的功能。
修改强>
根据下面的一些讨论,我们的comboFunc
函数处理空数组compN
是很有价值的。
[]
Yuck!此处的预期行为是var f = compN([]);
// Uncaught TypeError: Reduce of empty array with no initial value
创建一个返回未修改输入的函数。
compN
使用this作为reduce的起点将保证即使给出空数组,我们的function id(x) {
return x;
}
函数也能正常工作
compN
检查出来
// revised compN
function compN(fs) {
return fs.reduce(comp, id);
}
现在compN([])(2); // 2
compN([multi, add])(2); // 90
适用于0个或更多函数的数组。
答案 1 :(得分:1)
你调用它的方式(有两个参数),Array#reduce
为数组中的每个条目调用一次回调。
在第一次调用时,last
参数是您在结尾处给出的值(在您的情况下为input
),current
参数是数组中的第一个条目。你的回调是调用那个传入last
的条目(你的第一个函数),所以用2
调用第一个函数。由于这是add
,因此调用它的结果为3
,然后您将从回调中返回。
在第二次调用时,last
参数是您在上一次调用时返回的参数;所以它是3
。 current
参数是数组中的下一个函数。在您的情况下,那是multi
,因此您可以使用3
来调用它。 multi
会返回您返回的90
。
reduce
的结果是回调返回的最后一个值。所以,90
,在您的示例中。
您可以将reduce
的这种形式视为这样(不字面意思是它的作用,reduce
更复杂;这是一个简化版本):< / p>
// Again, this is a *simplified* example
function pseudoReduce(array, callback, initialValue) {
var index, last;
last = initialValue;
for (index = 0; index < array.length; ++index) {
last = callback(last, array[index], index, array);
}
return last;
}
(Array#reduce
如果你没有给第二个参数,那么在第一次传球时会有不同的略微 ;这不会反映在上面。)
答案 2 :(得分:1)
了解所发生情况的最简单方法是在代码中添加console.log()
,以便逐步了解正在发生的事情:
function comboFunc(num, functionsArr) {
return functionsArr.reduce(function (last, current) {
console.log(current + " " + last + " ==> " + current(last));
return current(last);
}, num);
}
function add(num) {
return num + 1;
}
function multi(num) {
return num * 30;
}
var functionsArr = [add, multi];
console.log( comboFunc(2, functionsArr) );
输出:
"function add(num) {
return num + 1;
} 2 ==> 3"
"function multi(num) {
return num * 30;
} 3 ==> 90"
90
这基本上是
multi(add(2)); // (2+1)*30 = 90