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