您好!
def foldLeft[B](z: B)(op: (B, A) => B): B =
var result = z
this foreach (x => result = op(result, x))
result
请你解释一下 - 这个foreach(x => 它是如何工作的?为什么没有使用地图?
答案 0 :(得分:2)
这是如何运作的?
首先,this foreach (f)
可以加糖到this.foreach(f)
(参见 Arity-1 部分here)。其次,foreach
期望具有“副作用”和Unit
返回值的函数。它为每个元素应用该函数一次,可能会更新某些“状态”。在这种情况下,请注意函数体(=>
运算符的右侧)为var result
分配一个新值:它会在每次迭代时更新,每次迭代都会更新使用前一个更新的值(从值z
开始)。
为什么不使用
map
?
map
生成一个新的集合作为返回值,这里根本不需要 - 我们只对这个匿名函数的“副作用”感兴趣(即更新result
的价值)而不是任何退回的集合。如果我们使用map
使用相同的函数,那么如果Unit
s ...将会导致无用的集合...
答案 1 :(得分:1)
map
用于迭代集合的所有成员,应用一些转换并返回另一个/相同的集合。虽然foreach
用于副作用。
这里我们在op
和连续的集合元素(即result
)上应用二元运算符x
,以获得类型B
的结果 not 必然是一个集合。这就是为什么不使用map
的原因。
答案 2 :(得分:0)
有两个答案:第一个是如果你在这里使用map
而不是foreach
会发生什么,这就是代码仍然有用。你只是在使用“太强大”的东西。您实际上并不关心foreach
中的表达式返回的内容,您只关心它在每次迭代中所做的副作用(对var
的更新)。
foreach
功能与命令式for
循环的功能相同。举一个C的例子,就像我们不写
// Nonsensical C
int x = for (int i = 0; i < 10; i++) {
x++;
}
而只是写
// We don't assign the loop
int x = 0;
for (int i = 0; i < 10; i++) {
x++;
}
回答问题的第二种方式来自Scala社区内的强大传统在执行map
时没有副作用(或者对集合进行任何类型的遍历,包括倍)。此规则的例外是返回Unit
的任何内容(例如foreach
),因为这些表达式将完全无用。
因此,考虑到这一传统,为什么甚至会产生这种副作用,而不仅仅是使用基于map
的实现。
如果我们不希望它有副作用,我们就无法在地图方面实施foldLeft
,除非我们有一个非常奇怪的map
版本。 Scala中的map
独立地作用于集合的每个元素(出于围绕parametricity的充分理由),而foldLeft
必须保留某种上下文(累加器),因为它遍历了它的集合可能会用来影响集合中每个元素的发生。除了集合的当前元素以及map
允许对过去的值的潜在依赖性之外,foldLeft
的这种独立性基本上是不一致的;后者严格来说更强大(你可以用map
来实现foldLeft
,见注释。)
这意味着为了实现折叠,我们必须使用更强大的技术来遍历数据结构。这归结为两种选择:
实现者在这里选择后者而不是递归。
注意:根据您对map
的定义,这里有一点点细微差别。如果您希望map
遵守仿函数法则,您可以创建一个带折叠的不一致map
,但只要符合map
版本存在,那么您可以使用倍。