您好,登錄后才能下訂單哦!
本篇內容介紹了“Scala的閉包概念是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
到本章這里,所有函數文本的例子僅參考了傳入的參數。例如,(x: Int) => x > 0里,函數體用到的***變量,x > 0,是x,被定義為函數參數。然而也可以參考定義在其它地方的變量:
(x: Int) => x + more // more是多少?
函數把“more”加入參考,但什么是more呢?從這個函數的視點來看,more是個自由變量:free variable,因為函數文本自身沒有給出其含義。相對的,x變量是一個綁定變量:bound variable,因為它在函數的上下文中有明確意義:被定義為函數的***參數,一個Int。如果你嘗試獨立使用這個函數文本,范圍內沒有任何more的定義,編譯器會報錯說:
scala> (x: Int) => x + more < console>:5: error: not found: value more (x: Int) => x + more ?
另一方面,只要有一個叫做more的什么東西同樣的函數文本將工作正常:
scala> var more = 1 more: Int = 1 scala> val addMore = (x: Int) => x + more addMore: (Int) => Int = < function> scala> addMore(10) res19: Int = 11
依照這個函數文本在運行時創建的函數值(對象)被稱為閉包:closure。名稱源自于通過“捕獲”自由變量的綁定對函數文本執行的“關閉”行動。不帶自由變量的函數文本,如(x: Int) => x + 1,被稱為封閉術語:closed term,這里術語:term指的是一小部分源代碼。因此依照這個函數文本在運行時創建的函數值嚴格意義上來講就不是閉包,因為(x: Int) => x + 1在編寫的時候就已經封閉了。但任何帶有自由變量的函數文本,如(x: Int) => x + more,都是開放術語:open term。因此,任何依照(x: Int) => x + more在運行期創建的函數值將必須捕獲它的自由變量,more,的綁定。由于函數值是關閉這個開放術語(x: Int) => x + more的行動的最終產物,得到的函數值將包含一個指向捕獲的more變量的參考,因此被稱為閉包。
這個例子帶來一個問題:如果more在閉包創建之后被改變了會發生什么事?Scala里,答案是閉包看到了這個變化。如下:
scala> more = 9999 more: Int = 9999 scala> addMore(10) res21: Int = 10009
直覺上,Scala的閉包捕獲了變量本身,而不是變量指向的值。相對的,Java的內部類根本不允許你訪問外圍范圍內可以改變的變量,因此到底是捕獲了變量還是捕獲了它當前具有的值就沒有差別了。就像前面演示的例子,依照(x: Int) => x + more創建的閉包看到了閉包之外做出的對more的變化。反過來也同樣。閉包對捕獲變量作出的改變在閉包之外也可見。下面是一個例子:
scala> val someNumbers = List(-11, -10, -5, 0, 5, 10) someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10) scala> var sum = 0 sum: Int = 0 scala> someNumbers.foreach(sum += _) scala> sum res23: Int = -11
例子用了一個循環的方式計算List的累加和。變量sum處于函數文本sum += _的外圍,函數文本把數累加到sum上。盡管這是一個在運行期改變sum的閉包,作為結果的累加值,-11,仍然在閉包之外可見。
如果閉包訪問了某些在程序運行時有若干不同備份的變量會怎樣?例如,如果閉包使用了某個函數的本地變量,并且函數被調用很多次會怎樣?每一次訪問使用的是變量的哪個實例?
僅有一個答案與語言余下的部分共存:使用的實例是那個在閉包被創建的時候活躍的。例如,以下是創建和返回“遞增”閉包的函數:
def makeIncreaser(more: Int) = (x: Int) => x + more
每次函數被調用時都會創建一個新閉包。每個閉包都會訪問閉包創建時活躍的more變量。
scala> val inc1 = makeIncreaser(1) inc1: (Int) => Int = < function> scala> val inc9999 = makeIncreaser(9999) inc9999: (Int) => Int = < function>
調用makeIncreaser(1)時,捕獲值1當作more的綁定的閉包被創建并返回。相似地,調用makeIncreaser(9999),捕獲值9999當作more的閉包被返回。當你把這些閉包應用到參數上(本例中,只有一個參數,x,必須被傳入),回來的結果依賴于閉包被創建時more是如何定義的:
scala> inc1(10) res24: Int = 11 scala> inc9999(10) res25: Int = 10009
盡管本例中more是一個已經返回的方法調用的參數也沒有區別。Scala編譯器在這種情況下重新安排了它以使得捕獲的參數繼續存在于堆中,而不是堆棧中,因此可以保留在創建它的方法調用之外。這種重新安排的工作都是自動關照的,因此你不需要操心。請任意捕獲你想要的變量:val,var,或參數。
“Scala的閉包概念是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。