我認爲有@tailrec
註釋可以確保編譯器會優化尾遞歸函數。你只是把它放在宣言前面?如果Scala用於腳本模式(例如,在REPL下使用:load <file>
),它也可以工作嗎?什麼是Scala註釋以確保尾部遞歸函數得到優化?
回答
從「Tail calls, @tailrec and trampolines」的博客文章:
- 在斯卡拉2.8,你也可以使用新的
@tailrec
註釋,以獲取有關該方法的優化的信息。
該註解允許您標記希望編譯器優化的特定方法。
如果編譯器未對其進行優化,則會發出警告。- 在Scala 2.7或更早版本中,您需要依靠手動測試或字節碼檢查來確定方法是否已經過優化。
例子:
您可以添加一個
@tailrec
註釋,這樣就可以確保你的修改工作。
import scala.annotation.tailrec
class Factorial2 {
def factorial(n: Int): Int = {
@tailrec def factorialAcc(acc: Int, n: Int): Int = {
if (n <= 1) acc
else factorialAcc(n * acc, n - 1)
}
factorialAcc(1, n)
}
}
它從REPL工作(例如,從Scala REPL tips and tricks):
C:\Prog\Scala\tests>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.annotation.tailrec
import scala.annotation.tailrec
scala> class Tails {
| @tailrec def boom(x: Int): Int = {
| if (x == 0) throw new Exception("boom!")
| else boom(x-1)+ 1
| }
| @tailrec def bang(x: Int): Int = {
| if (x == 0) throw new Exception("bang!")
| else bang(x-1)
| }
| }
<console>:9: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
@tailrec def boom(x: Int): Int = {
^
<console>:13: error: could not optimize @tailrec annotated method: it is neither private nor final so can be overridden
@tailrec def bang(x: Int): Int = {
^
Scala編譯器會自動優化任何真正的尾遞歸方法。如果您註釋了一種您認爲是使用@tailrec
註釋進行尾遞歸的方法,那麼編譯器會警告您,如果該方法實際上不是尾遞歸的。這使得註釋成爲一個好主意,這既是爲了確保一種方法目前可以優化,並且它在修改後仍然可以優化。
請注意,如果可以覆蓋,Scala不會考慮一種方法是尾遞歸的。因此,該方法必須是私有的,最終的,在對象上(而不是類或特徵),或者在另一種方法中被優化。
的註解是scala.annotation.tailrec
。它觸發如果該方法不能被尾調用優化這恰好如果編譯器錯誤,:
- 遞歸調用不是在尾部位置
- 所述方法可被覆寫
- 的方法,沒有最後(前面的特例)
它放在方法定義的def
之前。它在REPL中起作用。
這裏我們導入註釋,並嘗試將方法標記爲@tailrec
。
scala> import annotation.tailrec
import annotation.tailrec
scala> @tailrec def length(as: List[_]): Int = as match {
| case Nil => 0
| case head :: tail => 1 + length(tail)
| }
<console>:7: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
@tailrec def length(as: List[_]): Int = as match {
^
哎呀!最後一次調用是1.+()
,而不是length()
!讓我們重新制定方法:因爲它是另一種方法的範圍定義
scala> def length(as: List[_]): Int = {
| @tailrec def length0(as: List[_], tally: Int = 0): Int = as match {
| case Nil => tally
| case head :: tail => length0(tail, tally + 1)
| }
| length0(as)
| }
length: (as: List[_])Int
注意length0
是自動私有。
擴展你上面所說的,Scala只能優化單個方法的尾部調用。相互遞歸調用不會被優化。 – 2012-05-26 04:19:30
我討厭成爲挑剔的人,但在你的例子中,在無情況下,你應該返回一個正確的列表長度函數,否則當遞歸完成時你總會得到0作爲返回值。 – 2017-01-15 22:50:14
- 1. 部分尾遞歸函數是否仍然可以獲得完全尾遞歸函數的優化優勢?
- 2. 我該如何註釋這個尾部遞歸Scala函數
- 3. 在Scala中可以保證尾遞歸優化嗎?
- 4. 優化非尾遞歸函數
- 5. Memoizing尾調用優化遞歸函數
- 6. haskell可以優化不尾遞歸函數嗎?
- 7. Scheme中的遞歸函數總是進行尾調優化?
- 8. PHP是否優化尾遞歸?
- 9. 可以函數尾遞歸
- 10. 優化遞歸函數
- 11. 優化遞歸函數
- 12. Php遞歸函數優化
- 13. 尾遞歸函數
- 14. scala中沒有尾遞歸優化時堆棧溢出?
- 15. 爲什麼尾遞歸優化比Python中的正常遞歸更快?
- 16. 爲什麼scala不會進行尾部呼叫優化?
- 17. Scala @specialized註釋無限遞歸?
- 18. 我怎樣才能得到這個函數是尾遞歸?
- 19. 斯卡拉尾遞歸優化
- 20. C++代碼中的尾遞歸優化
- 21. 遞歸函數和註釋樹
- 22. 爲什麼不是這個F#內函數尾遞歸?
- 23. 爲什麼這個scala並置函數不是遞歸的?
- 24. 在非尾遞歸函數中使用Scala內部函數時會有效嗎?
- 25. Scala中的尾遞歸findNextAndTail
- 26. 什麼是尾遞歸消除?
- 27. 爲什麼不是這種尾遞歸?
- 28. 在F#/ Scala中優化相互遞歸的標準方法是什麼?
- 29. 什麼是Scala註釋實際上?
- 30. 尾遞歸函數總是要避免?
我想這有點像Java中的'override'註釋 - 代碼沒有它,但是如果你把它放在那裏,它會告訴你,如果你犯了一個錯誤。 – 2015-01-05 15:05:02