在Scala中需要后续函数调用的情况下是显式变量声明吗?

时间:2013-07-21 11:19:07

标签: scala

引用“Scala编程”:

//Code snippet 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)

//Code snippet 2    
def grep(pattern: String) =
  for {
    file <- filesHere
    if file.getName.endsWith(".scala")
    line <- fileLines(file)
    trimmed = line.trim /*********Question is about this line*********/
    if trimmed.matches(pattern)
  } println(file +": "+ trimmed)

引入trimmed的动机如下:

  

请注意,前面的代码重复了表达式line.trim。这是一个   非平凡的计算,所以你可能只想计算一次。

我在这样的情况下看到一些语句,这些变量是多余的,因为编译器会通过缓存或自己引入这样的变量来处理重复的函数调用,因此用户不应该为此烦恼。这是正确的还是我应该总是自己介绍这样一个变量? (并且Java在这方面与Scala不同吗?因为我看过有关Java而不是Scala的陈述。)

1 个答案:

答案 0 :(得分:3)

不,它不会自动缓存计算。应该怎么做?由于Scala没有效果系统,因此编译器不知道正在执行的函数是否具有副作用。因此,由于潜在的性能改进而不重复计算可能会导致不同的行为:

scala> for (i <- Option(5) if {println(i); i*i} < 50) yield {println(i); i*i}
5
5
res0: Option[Int] = Some(25)

scala> for (i <- Option(5); j = {println(i); i*i} if j < 50) yield j
5
res1: Option[Int] = Some(25)

顺便说一下,你总是可以检查编译器生成的代码:

$ scala -Xprint:typer -e "for (i <- Option(5) if {println(i); i*i} < 50) yield {println(i); i*i}"
[[syntax trees at end of                     typer]] // scalacmd5404327798073027065.scala
package <empty> {
  object Main extends scala.AnyRef {
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    };
    def main(argv: Array[String]): Unit = {
      val args: Array[String] = argv;
      {
        final class $anon extends scala.AnyRef {
          def <init>(): anonymous class $anon = {
            $anon.super.<init>();
            ()
          };
          scala.Option.apply[Int](5).withFilter(((i: Int) => {
            scala.this.Predef.println(i);
            i.*(i)
          }.<(50))).map[Int](((i: Int) => {
            scala.this.Predef.println(i);
            i.*(i)
          }))
        };
        {
          new $anon();
          ()
        }
      }
    }
  }
}

5
5