我正在使用Functional Programming in Scala書學習Scala。它的Github配套筆記說,a || go(x)仍然是一個尾部呼叫優化的遞回,而1 go(x)則不是。這種計算方式有什么不同,為什么編譯器可以優化一個而不是另一個?
uj5u.com熱心網友回復:
更簡潔地解釋一下:對于尾部遞回,最后的操作必須是遞回呼叫。
在1 go(x)中,最終操作是加法。
另一方面,||運算子是懶惰的:如果a為真,運算式a || go(x)只是評估為真;只有當a為假時,才評估go(x),所以go(x)是最終操作,因此它是尾遞回。
uj5u.com熱心網友回復:
假設你有以下方法。
def sumOfN(n: Int)。Int =
if (n <= 0)
0 0
else()
n sumOfN(n - 1)
現在,這個函式不是尾部遞回。為什么呢?讓我們看看sumOfN(3)的邏輯執行。
在執行這個函式時,堆疊將看起來像,
在執行這個函式時,堆疊將看起來像。
sumOfN(3)
3 sumOfN(2)
sumOfN(2)
3 sumOfN(2)
2 sumOfN(1)
3 sumOfN(2)
sumOfN( 1)
2 sumOfN(1)
3 sumOfN(2)
1 sumOfN(0)
2 sumOfN(1)
3 sumOfN(2)
sumOfN(0)
1 sumOfN(0)
2 sumOfN(1)
3 sumOfN(2)
0
1 sumOfN(0)
2 sumOfN(1)
3 sumOfN(2)
1 0
2 sumOfN(1)
3 sumOfN(2)
1
2 sumOfN(1)
3 sumOfN(2)
2 1
3 sumOfN(2)
3
3 sumOfN(2)
3 3
6
任何在堆疊上增長的遞回方法都不是尾部遞回。
但是,如果我們看一下那個布爾方法的例子
def isZeroSomewhere(n: Int)。Boolean =
(n == 0) || isZeroSomewhere(n - 1)
由于boolean OR的分支優化,這將是尾部遞回。這大概意味著將創建堆疊的兩個分支,帶有isZeroSomewhere(n - 1)的分支只有在(n == 0)為非真時才會被評估((n == 0)banch將被終止)。
isZeroSomewhere(3)
// branch 1 // branch2
(3 ==0) isZeroSomewhere(2)
// false,terminate // needs further evaluation。
isZeroSomewhere(2)
// branch 1 // branch2
(2 ==0) isZeroSomewhere(1)
// false,terminate // needs further evaluation。
isZeroSomewhere(1)
// branch 1 // branch2
(1 ==0) isZeroSomewhere(0)
// false,terminate // needs further evaluation。
isZeroSomewhere(0)
// branch 1 // branch2
(0 ==0) isZeroSomewhere(1)
// true // 另一個分支是真的,終止。
正如,你可以看到在這種情況下,堆疊大小根本沒有增長。所以,這就是尾部遞回。
請記住,這個分支優化是由左至右進行的。所以,下面這個方法不是尾部遞回。它實際上是一個左分支的無限回圈,并將永遠導致堆疊溢位。
def isZeroSomewhere(n: Int)。Boolean =
isZeroSomewhere(n - 1) || (n == 0)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/326316.html
標籤:
