Scala - 嵌套迭代(Odersky et al。,2010)

时间:2018-01-21 17:10:48

标签: scala nested-loops

以下代码来自Odersky等人。 (第167页)" 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.*")

如果没有它,代码将无法编译,因为&#34; Scala编译器在括号内不会推断出半冒号&#34; (同上,第167页)。 问题1:为什么代码不等同于:

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条件之后的分号?它起什么作用?

2 个答案:

答案 0 :(得分:2)

回答问题1 -

  1. for comprehension应该始终以&lt; - 开头,它的第一个语句为后面的剩余表达式创建上下文。

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

  3. 例如

    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)不在理解之内,而在于表达式。所以它不会编译。
    2. 回答问题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。它更加优雅地解释了理解。

答案 1 :(得分:1)

你的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)

使用大括号{ ... }括起代码块可以跳过显式分号的要求。