了解foldLeft实现

时间:2016-04-30 15:21:53

标签: scala fold

您好!

def foldLeft[B](z: B)(op: (B, A) => B): B = 
           var result = z
           this foreach (x => result = op(result, x))
           result

请你解释一下 - 这个foreach(x => 它是如何工作的?为什么没有使用地图

3 个答案:

答案 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版本存在,那么您可以使用倍。