Scala 为什么使用foldLeft而不是程序版本?
因此,在阅读过程中,有人指出,与《程序法典》不同的是:Scala 为什么使用foldLeft而不是程序版本?,scala,Scala,因此,在阅读过程中,有人指出,与《程序法典》不同的是: def expand(exp: String, replacements: Traversable[(String, String)]): String = { var result = exp for ((oldS, newS) <- replacements) result = result.replace(oldS, newS) result } 我几乎肯定会写第一个版本,因为熟悉程序或函数式风格的程序员可
def expand(exp: String, replacements: Traversable[(String, String)]): String = {
var result = exp
for ((oldS, newS) <- replacements)
result = result.replace(oldS, newS)
result
}
我几乎肯定会写第一个版本,因为熟悉程序或函数式风格的程序员可以轻松阅读和理解它,而只有熟悉函数式风格的程序员才能轻松阅读和理解第二个版本
但是暂时把可读性放在一边,是否有什么东西使得foldLeft
比程序版本更好呢?我可能认为它会更有效,但事实证明,它实际上只是上面的过程代码。那么,这只是一种风格选择,还是有充分的理由使用其中一种版本
编辑:我想说清楚,我不是问其他函数,只是问foldLeft
。我对使用foreach
、map
、filter
等功能非常满意,这些功能都可以很好地映射到地图上,便于理解
答案:这里有两个很好的答案(由和提供),尽管我只能接受一个:
- 使用
,因为还有额外的优化,例如使用while循环比for循环更快foldLeft
- 如果它被添加到常规集合中,请使用
,因为这将使到并行集合的转换变得微不足道fold
如果foldLeft的实现应该得到一些特殊的好处,比如说额外的优化,你可以免费得到这些,而不需要更新无数的方法。除了讨厌可变变量(甚至可变局部变量),在这种情况下使用fold的基本原因是清晰,偶尔简洁。fold版本的大部分冗长之处在于,您必须使用带有解构绑定的显式函数定义。如果列表中的每个元素在折叠操作中仅使用一次(常见情况),则可以简化为使用缩写形式。因此,一组数字之和的经典定义
collection.foldLeft(0)(_+_)
比任何等价的命令式构造都要简单和简短得多
使用函数收集操作的另一个元原因(虽然在本例中不直接适用)是,如果性能需要,可以使用并行收集操作。Fold不能并行化,但Fold操作通常可以转化为可交换的关联reduce操作,并且这些操作可以并行化。在Scala 2.9中,利用多个处理核心将非并行功能更改为并行功能有时就像将
.par
放到要对其执行并行操作的集合上一样简单 这里有一个我还没有看到的词是:
声明式编程通常被定义为任何非必需的编程风格。还有许多其他常见的定义试图给术语下一个定义,而不是简单地将其与命令式编程进行对比。例如:
- 一种程序,描述应该执行什么计算,而不描述如何计算
- 任何没有副作用的编程语言(或者更具体地说,是引用透明的)
- 与数学逻辑有明确对应关系的语言
// Sugar-free Scala (Still better than Java<5)
def sumDoubled1(xs: List[Int]) = {
var sum = 0 // Initialized correctly?
for (i <- 0 until xs.size) { // Fenceposts?
sum = sum + (xs(i) * 2) // Correct value being extracted?
// Value extraction and +/* smashed together
}
sum // Correct value returned?
}
// Iteration sugar (similar to Java 5)
def sumDoubled2(xs: List[Int]) = {
var sum = 0
for (x <- xs) // We don't need to worry about fenceposts or
sum = sum + (x * 2) // value extraction anymore; that's progress
sum
}
// Verbose Scala
def sumDoubled3(xs: List[Int]) = xs.map((x: Int) => x*2). // the doubling
reduceLeft((x: Int, y: Int) => x+y) // the addition
// Idiomatic Scala
def sumDoubled4(xs: List[Int]) = xs.map(_*2).reduceLeft(_+_)
// ^ the doubling ^
// \ the addition
//无糖Scala(仍然优于Java值得指出的是,还有另一种调用foldLeft
的方法,它利用了以下优点:
- 在标识符中使用(几乎)任何Unicode符号的能力
- 一种功能,如果方法名以冒号结尾
:
,称为中缀,则切换目标和参数
对我来说,这个版本更清晰,因为我可以看到我正在将expr
值折叠到replacements
集合中
def expand(expr: String, replacements: Traversable[(String, String)]): String = {
(expr /: replacements) { case (r, (o, n)) => r.replace(o, n) }
}
我想关于foldLeft
的未来优化的观点是好的,尽管很难想象那些可能是什么样的优化……仔细想想,一个优化是使用while
循环,这通常比foreach
更快。我查看了它,IndexedSeqOptimized使用了一个递归尾部foldLeft
的定义可能和while循环一样有效。所以我想我是在额外的优化参数上被说服的;-,但没有collection.sum
那么简单;-)那么在Scala 2.9中,并行集合中的foldLeft
会发生什么呢?我相信foldLeft
和foldRight
只是顺次实现。考虑到它们的普遍性,它们也没什么别的办法了。另一方面,在并行集合上也有一种fold
方法。它要求该操作采用与集合相同类型的两个元素,并假定该操作是关联的。这将尝试在任何可用的内核上自动并行化操作。我看到并行集合中存在折叠
def expand(expr: String, replacements: Traversable[(String, String)]): String = {
(expr /: replacements) { case (r, (o, n)) => r.replace(o, n) }
}