2013-07-21 37 views
0

引用 '編程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)。

回答

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 
+0

謝謝你,這是相當全面的答案。我是一個Scala新手,但我很清楚,很清楚。 – tkroman

相關問題