鍍金池/ 問答/Scala  網(wǎng)絡安全/ scala 閉包問題

scala 閉包問題

scala 的三個函數(shù)如下所示,正確調(diào)用三個函數(shù)返回的函數(shù)簽名是一樣的,但是不明白三個函數(shù)不同之處到底代表什么?

直觀上看 f1 和 f3 都是不接受任何參數(shù),但是使用上 f1 可以使用省略括號和使用空括號兩種方式,f3 則不能使用空括號。f1 和 f3 兩個函數(shù)有什么區(qū)別呢?這種區(qū)別是 scala 的特性呢還是bug呢?

f1 和 f2 函數(shù)體內(nèi)返回 pp 函數(shù)的時候使用不使用 “_“ 都是返回了函數(shù)。不使用 "_" 也并沒有當成函數(shù)調(diào)用去編譯然后報錯。請問這個該怎么理解呢?

def f1(): String => Unit = {
  def pp(s: String): Unit = println(s)
  pp
}

def f2(): String => Unit = {
  def pp(s: String): Unit = println(s)
  pp _
}

def f3: String => Unit = {
  def pp(s: String): Unit = println(s)
  pp
}
回答
編輯回答
艷骨

沒人回答就自問自答吧。
_ 我感覺就是個包裝器,類似于

def f(s:String)(r:String) = println(s"$s passed, and then $r passed")

val a = f("hello") _

就相當于

val a = (r:String) => f("hello")(r)

對于 val b = 4 使用 val c = b _ 也會使得c成為一個匿名函數(shù),函數(shù)簽名為 ()=>Int
但是具體的機制怎么樣還沒弄明白。。。
對于函數(shù) f3,是關于 pp 的使用,pp 編譯器怎么編譯 pp 取決于 pp 出現(xiàn)的位置。pp 首先是一個對象,function1 之類的對象。有些地方會把 pp 當作函數(shù)調(diào)用,有些地方會把 pp 當作 函數(shù)對象。

這兩天寫代碼的一些感想,不嚴格。。。

2018年4月14日 13:52
編輯回答
近義詞

首先,要明確在 Scala 中如果一個方法不接受參數(shù),那么可以不使用 () 調(diào)用它:

scala> 2.toString()
res1: String = 2

等價于:

scala> 2.toString

但是在定義函數(shù)時,方法是否有括號則很重要。對于樓主的函數(shù):

def f1(): String => Unit = {
  def pp(s: String): Unit = println(s)
  pp
}

def f3: String => Unit = {
  def pp(s: String): Unit = println(s)
  pp
}

編譯器認為 f1f3 不等價。對于 f1 我們可以不加括號調(diào)用它,也可以加上括號;但是 f3 就一定不能加上括號。這是一個 feature 而不是 bug可以參考我的這篇文章:有括號方法和無括號方法區(qū)別。在結構類型(Structural Type)中,這兩者定義是有區(qū)別的。

再談談 f1f2 的區(qū)別。實際上 f1 返回的函數(shù)是 ppf2 返回的是一個匿名函數(shù),這樣匿名函數(shù)其實是:

s => pp(s)

這就是 _ 的作用。它實際上是一個占位符,表示接受一個參數(shù)。樓主也可以寫成:

def f2(): String => Unit = {
  def pp(s: String): Unit = println(s)
  pp(_)  // 返回一個匿名函數(shù),而不是 pp 了
}

回到樓主的例子中:

val c = b _

就等價于:

val c = () => b

另外,這三個函數(shù)在我理解看來不是閉包哦。閉包可以理解為在函數(shù)中引用了函數(shù)外部的變量,

2017年3月21日 10:18