2012-03-15 85 views
2

我有一個函數,我知道是尾遞歸。但是由於我定義它的方式,編譯器會抱怨函數在非尾部位置有遞歸調用。這是功能。我該如何註釋這個尾部遞歸Scala函數

@tailrec 
def travel: (Int, List[Char]) => Int = { 
    case (n,  Nil) => n 
    case (n, '~' :: sls) => travel(0, sls) 
    case (n, '^' :: sls) => travel(max(n-1,0), sls) 
    case (n, '>' :: sls) => travel(n+1, sls) 
    case (_, s :: sls) => throw new IllegalArgumentException("Illegal selector '" + s + "'") 
} 

我得到

error: could not optimize @tailrec annotated method travel: it contains a recursive call not in tail position 
def travel: (Int, List[Char]) => Int = { 

如果我把它寫成這樣,它工作正常。

@tailrec 
def travel(n:Int, l:List[Char]): Int = (n,l) match { 
    case (n,  Nil) => n 
    case (n, '~' :: sls) => travel(0, sls) 
    case (n, '^' :: sls) => travel(max(n-1,0), sls) 
    case (n, '>' :: sls) => travel(n+1, sls) 
    case (_, s :: sls) => throw new IllegalArgumentException("Illegal selector '" + s + "'") 
} 

我認爲這跟def: (Input) => Output = {}類型的聲明風格有關。我使用它,因爲它看起來比編寫嵌套匹配更清晰,或者是元組上的匹配。

+0

我認爲整個問題可以重新格式化爲:*「我們可以添加一個匿名函數的@ tailrec註釋嗎?」* – paradigmatic 2012-03-15 16:07:08

+0

@paradigmatic - 這是一個不同的問題,但一個很好的問題。不是註釋,而是匿名函數進行尾遞歸調用的一種方式。 (它看不到它自己的「應用」方法。) – 2012-03-15 17:59:07

回答

7

兩者並不相同。在第一種情況下,方法產生一個函數,然後再次調用該方法(產生函數等)。也就是說,每次在第一種情況下致電travel時,都會創建一個新實例Function1[(Int, List[Char]), Int]。不出所料,這不能轉化爲跳轉指令。 (理論上可以,但分析將會非常複雜,因爲人們不得不取消所有這些對象創作。)

在第二種情況下,它只是一種調用自身的方法,可以將其轉換爲跳。