1. 程式人生 > >淺析scala傳名呼叫和傳值呼叫,: => 與() : =>

淺析scala傳名呼叫和傳值呼叫,: => 與() : =>

 函式呼叫一般傳值呼叫,但是,在某些情況下,我們不希望函式的值首先被計算,而是等到呼叫的時候再來進行計算,為了適應這種情景,scala提供了傳名呼叫。
先來看兩個例子: 
package test

/**
  * Created by layne on 05/05/17.
  */
object Test {
  def byValue(code: () => Unit): Unit = {
    println("start")
    code() //1
    println("end")
  }
  def byName(code: => Unit): Unit = {
    println("start"
) code println("end") } def main(args: Array[String]): Unit = { byValue { //2 println("calculated") () => println("test1") } println("------------------------") byName { println("uncalculated") println("test2") } } }
  • 註釋1:這裡必須用code(),這裡函式值經過scala直譯器翻譯成了一個Function0的例項物件,而code存了這個物件的引用,實際上是code.apply()方法的呼叫
  • 註釋2:這裡函式值將先被計算,計算結果恰好是一個簽名為 () => Unit 的匿名函式,如果計算結果為其他形式的話,這裡將不能編譯

    執行結果為:

calculated
start
test1
end
------------------------
start
uncalculated
test2
end

這裡我們明顯看出來byValue中程式碼在傳入前就被計算了,而byName中的是在呼叫時才進行計算。

再來一個例子:

package layne.blog.byName

/**
  * Created by layne on 08/05/17.
  */
object MyAssert {

  def
nameAssert(predicate: => Boolean, enableAssert: Boolean):
Unit = { if (enableAssert && !predicate) throw new Error("斷言錯誤") } def valueAssert(predicate: Boolean, enableAssert: Boolean): Unit = { if (!enableAssert && predicate) throw new Error("斷言錯誤") } def main(args: Array[String]): Unit = { //禁止斷言 //1 nameAssert(1 / 0 > 0, false) //禁止斷言的情況下,傳名呼叫不會報錯,因為predicate沒有被計算 //2 //valueAssert(1/0 > 0,false) //允許斷言 //3 //nameAssert(1/0 >0 ,true) } }

依次執行上述函式結果

1:沒有任何輸出
2:Exception in thread "main" java.lang.ArithmeticException: / by zero
    at layne.blog.byName.MyAssert$.main(MyAssert.scala:19)
    at layne.blog.byName.MyAssert.main(MyAssert.scala)
3:Exception in thread "main" java.lang.ArithmeticException: / by zero
    at layne.blog.byName.MyAssert$$anonfun$main$1.apply$mcZ$sp(MyAssert.scala:21)
    at layne.blog.byName.MyAssert$.nameAssert(MyAssert.scala:9)
    at layne.blog.byName.MyAssert$.main(MyAssert.scala:21)
    at layne.blog.byName.MyAssert.main(MyAssert.scala)
    23雖然都報了異常,但是請注意異常資訊的層級,同樣印證了nameAssert是在呼叫時進行計算的

最後來一個實際有用的例子:

package layne.blog.byName

/**
  * Created by layne on 08/05/17.
  */
object TestUntil {
  /**
    * 與while作用相反
    * @param condition
    * @param block
    */
  def until(condition: => Boolean)(block: => Unit): Unit = {
    //執行
    if (!condition) {
      block
      //傳名呼叫
      until(condition)(block)
    }
  }

  def main(args: Array[String]): Unit = {
    var i = 0
    //這裡利用柯里化和單引數呼叫能將()改成{},寫出形如內建關鍵字的程式碼
    until(i > 4) {//直到i>4就停下來
      println(i)
      i += 1
    }
  }
}

最近在自學scala,順便嘗試寫寫部落格。希望給同樣在學的童鞋們一點幫助,也希望各位老鳥給點指導!