Scala - 嵌套迭代(Odersky 等人,2010 年)

Scala - Nested Iteration (Odersky et al., 2010)

以下代码来自 Odersky 等人。 (第 167 页)于 "Programming in Scala"。

val filesHere = (new java.io.File(".")).listFiles

def fileLines(file: java.io.File) =
   scala.io.Source.fromFile(file).getLines().toList
def grep(pattern: String) = 
   for (
     file <- filesHere
     if file.getName.endsWith(".scala");
     line <- fileLines(file)
     if line.trim.matches(pattern)
 ) println(file +": "+ line.trim)
grep(".*gcd.*")

没有它代码将无法编译,因为 "the Scala compiler will not infer semi-colons while inside parentheses"(同上,第 167 页)。 问题一:为什么代码不等同于:

def grep(pattern: String) = 
   for (
     file <- filesHere
     if (file.getName.endsWith(".scala")){
       line <- fileLines(file)
     }
     if line.trim.matches(pattern)
 ) println(file +": "+ line.trim)
grep(".*gcd.*")

问题2:为什么上面代码中需要第一个if条件后有分号?有什么作用?

问题 1 的答案 -

  1. for comprehension 在其第一个语句中始终以 <- 开头,该语句为后面的其余表达式创建上下文。

  2. 理解中的所有 <- flatMap 期待最后一个 map。所有如果 filter.

例如

for (
    file <- filesHere
    if file.getName.endsWith(".scala")
    contents <- file.getContents
) yield (contents)

这相当于fileHere.flatMap(file).filter(_.endsWith(".scala")).map(_.getContents)

  1. 每个<-表达式都应该直接放在下面以便理解。您可以编写嵌套的理解。 line <- fileLines(file) 不在理解之内,而是在 if 表达式之内。所以它不会编译。

问题 2 的答案-

if 或 scala

中的任何表达式都不需要分号

以下代码有效

  val filesHere = (new java.io.File(".")).listFiles

  def fileLines(file: java.io.File) =
  scala.io.Source.fromFile(file).getLines().toList

  def grep1(pattern: String) = for {
    file <- filesHere
    if file.getName.endsWith(".scala")
    line <- fileLines(file)
    if line.trim.matches(pattern)
   } println(file +": "+ line.trim)

我推荐这个 tutorial。它更优雅地解释了理解。

您的 for 循环有几个 generators 和过滤器:

for (
  file <- filesHere                    // generator
  if file.getName.endsWith(".scala");  // filter
  line <- fileLines(file)              // generator
  if line.trim.matches(pattern)        // filter
) println(file + ": " + line.trim)

在 Scala 中,生成器和相应的过滤器可以放在 for comprehension 中。此 link 可能会提供有关该主题的更多详细信息。

至于分号,正如编译器所说的那样需要分号:the Scala compiler will not infer semi-colons while inside parentheses.

您的 for 循环与以下内容没有区别:

for (
  file <- filesHere if file.getName.endsWith(".scala");
  line <- fileLines(file) if line.trim.matches(pattern)
) println(file + ": " + line.trim)

用大括号括起代码块 { ... } 允许您跳过显式分号的要求。