[英]Scala sum of a binary tree tail recursive
所以我有以下代碼定義二叉樹。
sealed abstract class BinTree {
def sum = sumAcc(0)
def sumAcc(acc: Int): Int
}
case class NonEmpty(val elem: Int, val left: BinTree, val right: BinTree) extends BinTree {
def sumAcc(acc: Int) = right.sumAcc(left.sumAcc(elem + acc))
}
case object Empty extends BinTree {
def sumAcc(acc: Int) = acc
}
val rootTree = NonEmpty(1, NonEmpty(2, NonEmpty(3, Empty, Empty), Empty), Empty)
rootTree.sum
sum
方法尾遞歸嗎? 我懷疑是不是尾遞歸,因為對right.sumAcc
的調用必須等待left.sumAcc(elem + acc)
終止。
如果它不是尾遞歸,我怎么能改變呢?
好的,所以使用注釋中指出的解決方案 ,我重寫了尾部遞歸的答案。 我在輔助方法中使用了累加器和隱式堆棧。 通過注釋,我們可以檢查實際上該方法是尾遞歸的。
這是新代碼,以防有人發現它有用。
import scala.annotation.tailrec
sealed abstract class BinTree
case object Empty extends BinTree
case class NonEmpty(val elem: Int, val left: BinTree, val right: BinTree) extends BinTree
val rootTree = NonEmpty(1, NonEmpty(2, NonEmpty(3, Empty, Empty), Empty), Empty)
def sumTailRec(bt: BinTree) = {
@tailrec
def sumAccStack(trees: List[BinTree], acc: Int): Int = trees match {
case Nil => acc
case Empty :: rs => sumAccStack(rs, acc)
case NonEmpty(e, l, r) :: rs => sumAccStack(l :: r :: rs, e + acc)
}
sumAccStack(List(bt), 0)
}
sumTailRec(rootTree)
在你的情況下,函數不是尾遞歸,但原因有點不同 - 沒有等待,程序按順序執行, left.sumAcc
在right.sumAcc
之前right.sumAcc
,但這仍然是一個問題, left.sumAcc
調用是重復的,但不在尾部位置。 即使你要刪除它,你的解決方案還有其他問題。
要檢查是否可以對函數應用尾遞歸,可以使用@tailrec批注 。 在您的情況下,編譯器錯誤消息將是:
錯誤:(11,7)無法優化@tailrec注釋方法sumAcc:它既不是私有也不是最終的,所以可以被覆蓋
def sumAcc(acc:Int)= right.sumAcc(left.sumAcc(elem + acc))
為什么會發生這種情況解釋為什么Scala編譯器不會應用尾調用優化,除非方法是最終的? 題:
除非方法標記為final, 否則在進行遞歸調用時可能不會調用自身 。
如果您嘗試將final添加到NonEmpty
sumAcc
,編譯器也會告訴您。 錯誤消息將更改為:
錯誤:(11,38)無法優化@tailrec帶注釋的方法sumAcc:它包含一個針對超類型NonEmpty.this.right.type的遞歸調用
final def sumAcc(acc:Int)= right.sumAcc(left.sumAcc(elem + acc))
Millie Smith在評論中鏈接的解決方案通過不使用重寫來實現函數來克服這個問題,而是使用模式匹配(即函數中的類型檢查),因此一個函數正在處理Leaf
和Node
類型的遍歷,以及函數的結構使得在每種情況下只對sumAcc
進行一次調用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.