Scala的理解效率?

Scala的理解效率?,scala,for-comprehension,Scala,For Comprehension,在第23章“Scala编程”一书中,作者给出了如下示例: case class Book(title: String, authors: String*) val books: List[Book] = // list of books, omitted here // find all authors who have published at least two books for (b1 <- books; b2 <- books if b1 != b2; a1 <

在第23章“Scala编程”一书中,作者给出了如下示例:

case class Book(title: String, authors: String*)
val books: List[Book] = // list of books, omitted here
// find all authors who have published at least two books

for (b1 <- books; b2 <- books if b1 != b2;
    a1 <- b1.authors; a2 <- b2.authors if a1 == a2)
yield a1
但是如果查看map和flatmap方法定义(TraversableLike.scala),您可能会发现,它们被定义为for循环:

   def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    val b = bf(repr)
    b.sizeHint(this) 
    for (x <- this) b += f(x)
    b.result
  }

  def flatMap[B, That](f: A => Traversable[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    val b = bf(repr)
    for (x <- this) b ++= f(x)
    b.result
  }
defmap[B,That](f:A=>B)(隐式bf:CanBuildFrom[Repr,B,That]):That={
val b=bf(报告)
b、 sizeHint(本)
对于(x可遍历[B])(隐式bf:CanBuildFrom[Repr,B,That]):That={
val b=bf(报告)

对于(x我写代码是为了让它易于理解和维护。然后我会分析。如果有瓶颈,我会集中注意力。如果是像你描述的那样,我会以不同的方式解决问题。在那之前,我喜欢“糖”它省去了我写东西或认真思考的麻烦。

实际上有6个循环。每个过滤器/flatMap/map有一个循环

过滤器->映射对可以通过使用集合的惰性视图在一个循环中完成(迭代器方法)

一般来说,tt对书籍运行两个嵌套循环以查找所有书籍对,然后运行两个嵌套循环以查找一本书的作者是否在另一本书的作者列表中

使用简单的数据结构,显式编码时也会这样做

当然,这里的示例是显示一个复杂的“for”循环,而不是编写最有效的代码。例如,可以使用一个集合,而不是一系列作者,然后查找交叉点是否为非空:

for (b1 <- books; b2 <- books; a <- (b1.authors & b2.authors)) yield a

for(b1注意,在2.8中,
filter
调用被更改为
withFilter
,这是惰性的,可以避免构建中间结构。请参阅

我认为将
for
转换为
map
flatMap
with filter
(以及值定义,如果有的话)的原因是为了更容易使用monad


一般来说,我认为如果你正在进行的计算涉及到4次循环,那么使用
for
循环就可以了。如果计算可以更有效地进行并且性能很重要,那么你应该使用更有效的算法。

对于理解来说,是一元转换的语法糖分,因此非常有用在任何地方,它们在Scala中都比等价的Haskell构造要详细得多(当然,Haskell在默认情况下是非严格的,所以不能像在Scala中那样谈论构造的性能)

同样重要的是,此构造保持了正在执行的操作的清晰性,并避免快速升级缩进或不必要的私有方法嵌套

至于最后的考虑,无论这是否隐藏了复杂性,我将假设:

for {
  b1 <- books
  b2 <- books
  if b1 != b2
  a1 <- b1.authors
  a2 <- b2.authors 
  if a1 == a2
} yield a1
翻译成

List(1, 2, 3) map (x => x * 2)
而这与

map(List(1, 2, 3), ((x: Int) => x * 2)))
这就是调用您传递的定义的方式。根据记录,
List
上的
map
的实际实现是:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
  val b = bf(repr)
  b.sizeHint(this) 
  for (x <- this) b += f(x)
  b.result
}
defmap[B,That](f:A=>B)(隐式bf:CanBuildFrom[Repr,B,That]):That={
val b=bf(报告)
b、 sizeHint(本)

对于(x关于@IttayD关于算法效率的回答的一个后续问题。值得注意的是,该算法在原始帖子(以及书中)中是一个嵌套循环联接。实际上,对于大型数据集来说,这不是一个有效的算法,大多数数据库将在此处使用哈希聚合。在Scala中,哈希聚合类似于:

(for (book <- books;
      author <- book.authors) yield (book, author)
).groupBy(_._2).filter(_._2.size > 1).keys

(用于(书中你忘记了if b1!=b2条件。IMHO,为什么人们从不怀疑这可能是Scala的问题?我记得java中有一个名为Lambda4j的库,它可以让java包含闭包功能并编写DSL风格的代码。它大量利用了内省和动态代理,你可以用它编写非常高级的代码,但是我会付出性能上的代价。在探索Scala几个月后,我发现Scala就像Lambda4j一样,让我觉得我不是在使用一种语言,而是一个库,高级特性是用基本结构过度封装的,而不是语言设计的优势。@ZZCat内省和动态代理的成本很高JVM,但基本上Scala都不使用。根据我目前的理解,Scala只是模拟了一些函数式编程语言(Hashell)的功能,所有的转换都类似于方法调用。您可以定义一个名为“for”的方法,并将任务委托给正文中的while语句,但它没有这样做,因为它需要“for”更自然。Haskell的monad理解也被转换为方法调用。只需说“@ZZCat”它不“模拟”任何东西。它将语言转换为其他语言,就像其他所有编译语言一样。否则,我们可以说每种面向对象语言“模拟”“OO,因为它将其转换为机器代码。您提到的map/flatMap/filter定义来自哪里?@Daniel嗯,这些定义来自“Scala编程”的第23.5章。”-Martin Ordersky。是的,很难说明这个问题,我已经将它改为TraversableLike.scala定义。对于那些没有“scala中的编程”的人,请参考中的10.4和10.5@卷感谢您的追赶。请注意,它们被定义为
foreach
循环。这是意料之中的事情,因为这是人们需要在
可遍历
上实现的唯一方法。
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
  val b = bf(repr)
  b.sizeHint(this) 
  for (x <- this) b += f(x)
  b.result
}
(for (book <- books;
      author <- book.authors) yield (book, author)
).groupBy(_._2).filter(_._2.size > 1).keys