0

引用“在 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)。

4

1 回答 1

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
于 2013-07-21T11:26:16.697 回答