Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2008/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 为什么使用foldLeft而不是程序版本?_Scala - Fatal编程技术网

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
等功能非常满意,这些功能都可以很好地映射到地图上,便于理解

答案:这里有两个很好的答案(由和提供),尽管我只能接受一个:

  • 使用
    foldLeft
    ,因为还有额外的优化,例如使用while循环比for循环更快
  • 如果它被添加到常规集合中,请使用
    fold
    ,因为这将使到并行集合的转换变得微不足道

它更短更清晰-是的,你需要知道折叠是什么来理解它,但是当你用一种50%功能性的语言编程时,你无论如何都应该知道这些基本的构建块。折叠正是过程代码所做的(重复应用一个操作),但它被赋予了一个名称和通用性。虽然这只是一个小轮子,但它仍然是一个轮子


如果foldLeft的实现应该得到一些特殊的好处,比如说额外的优化,你可以免费得到这些,而不需要更新无数的方法。

除了讨厌可变变量(甚至可变局部变量),在这种情况下使用fold的基本原因是清晰,偶尔简洁。fold版本的大部分冗长之处在于,您必须使用带有解构绑定的显式函数定义。如果列表中的每个元素在折叠操作中仅使用一次(常见情况),则可以简化为使用缩写形式。因此,一组数字之和的经典定义

collection.foldLeft(0)(_+_) 
比任何等价的命令式构造都要简单和简短得多


使用函数收集操作的另一个元原因(虽然在本例中不直接适用)是,如果性能需要,可以使用并行收集操作。Fold不能并行化,但Fold操作通常可以转化为可交换的关联reduce操作,并且这些操作可以并行化。在Scala 2.9中,利用多个处理核心将非并行功能更改为并行功能有时就像将
.par
放到要对其执行并行操作的集合上一样简单

这里有一个我还没有看到的词是

声明式编程通常被定义为任何非必需的编程风格。还有许多其他常见的定义试图给术语下一个定义,而不是简单地将其与命令式编程进行对比。例如:

  • 一种程序,描述应该执行什么计算,而不描述如何计算
  • 任何没有副作用的编程语言(或者更具体地说,是引用透明的)
  • 与数学逻辑有明确对应关系的语言
这些定义基本上重叠

高阶函数(HOF)是声明性的关键促成因素,因为我们只指定what(例如,“使用此值集合,将每个值乘以2,求和结果”),而不指定how(例如初始化累加器,使用for循环进行迭代,从集合中提取值,添加到累加器…)

比较以下各项:

// 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) }
}