尾遞歸效率更高,因爲它重用相同的堆棧幀而不是創建一個新的,但爲什麼這需要在方案中的所有內容?爲什麼在方案的所有實現中都需要尾遞歸?
回答
尾遞歸由語言的規範來強制執行。從R6RS的5.11部分引用:
Scheme的實現必須是正確的尾遞歸。在某些稱爲尾部上下文的語法上下文中發生的過程調用是尾部調用。如果Scheme支持無限數量的活動尾部調用,則Scheme實現是正確的尾遞歸。如果被叫程序仍可能返回,則該呼叫處於活動狀態。請注意,這包括常規回報以及之前通過隨後調用的電流繼續捕獲的延續進行的返回。在沒有被捕獲延續的情況下,呼叫最多可以返回一次,而主動呼叫將是那些尚未返回的呼叫。在Clinger的論文[5]中可以找到正確的尾遞歸正式定義。第11.20節描述了從(rnrs base(6))庫中識別結構中尾部調用的規則。
實際的原因是,尾遞歸允許使用遞歸實現有效的循環。
Scheme沒有goto
,所以所有循環最終都是使用尾遞歸完成的。如果沒有適當的尾部遞歸保證,在Scheme中沒有可靠的方法來提供循環。
更新:我想解釋一下我所說的「最終使用尾遞歸」。我們來看看do
宏,因爲@newacct提到了它。例如:
(do ((i 1 (+ i 1)))
((> i 10))
(display i)
(newline))
正如我所說,方案沒有goto
,所以它必須得到它從什麼地方循環。它實際上是宏觀擴展到(像)這樣的:
(let loop ((i 1))
(unless (> i 10)
(display i)
(newline)
(loop (+ i 1))))
注意loop
這裏不是關鍵字或內置函數。這是一個命名函數,它由名爲let
†創建,並且正在unless
表單底部調用(通過尾遞歸)。
真的,Scheme中的所有標準循環形式都使用尾遞歸。有沒有擺脫它。
†這裏是名爲let
(嚴格地講‡)宏觀擴展爲:
(letrec ((loop (lambda (i)
(unless (> i 10)
(display i)
(newline)
(loop (+ i 1))))))
(loop 1))
‡嚴格地說,它的宏觀擴展爲:
((letrec ((loop (lambda (i)
(unless (> i 10)
(display i)
(newline)
(loop (+ i 1))))))
loop)
1)
計劃中有'do',但很少使用 – newacct 2013-02-18 23:59:30
@newacct'do'不使用'goto'。它在一個命名的'let'上宏擴展爲尾遞歸。 – 2013-02-19 04:21:00
[R5RS](http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-2.html#%_toc_%_sec_4.2.4)表示「Do是迭代構造」。它可以作爲一流的實施,不一定是一個宏觀。看起來,它與名爲let的語言一樣多。並且Scheme具有'call/cc',它是在類固醇上運行的。事實上*這可能是真正的原因 - 沒有TR保證'call/cc'是不可用的。 – 2013-02-19 17:23:34
沒有TR保證call/cc
將無法使用。循環本身可以通過do
which is a part of the language來實現。
編輯:原來這是不正確的。請參閱John Clements在該問題上的評論。一種語言可以具有非TR函數調用機制,但是具有用於調用繼續的特殊分離機制。
說到這,在這裏,尾巴遞進:http://instagram.com/p/NObwZSkEAk/ ;-) – 2013-02-19 17:27:43
@ ChrisJester-Young Nicce! :)唉,它是*你的*! – 2013-02-19 17:29:03
- 1. 方案尾遞歸
- 2. 爲什麼不將OCaml List.fold_right作爲尾遞歸實現?
- 3. 方案尾遞歸/迭代
- 4. 一個遞歸方案的庫實現
- 5. 所有遞歸結構都可以被非遞歸解決方案替代嗎?
- 6. 當所有需要的都是'require_once'時,'require'需要什麼?
- 7. 爲什麼我需要我需要在子類中實現IDisposable()
- 8. 爲什麼需要令牌方案?
- 9. 爲什麼AuthenticationHeaderValue需要該方案?
- 10. ruby中的尾遞歸 - 這兩個實現之間有什麼區別?
- 11. 遞歸需要的所有文件
- 12. 這個尾巴爲什麼遞歸?
- 13. 爲什麼不是這種尾遞歸?
- 14. 有關ANTLR中左遞歸的錯誤。現在需要做什麼?
- 15. 爲什麼我需要現實生活中的私人方法?
- 16. 尾遞歸是否需要累加器?
- 17. 爲什麼所有這些都需要JSON?
- 18. 爲什麼在Scheme和Python中實現遞歸方程的返回值不同?
- 19. 爲什麼chmod上的遞歸模式除了遞歸之外什麼都做?
- 20. 方法的遞歸實現
- 21. 方案遞歸
- 22. 在java中這種方法有什麼問題?我想實現遞歸
- 23. 遞歸的方案
- 24. 遞歸的方案
- 25. 爲什麼我的遞歸解決方案打印重複?
- 26. 爲什麼尾遞歸優化比Python中的正常遞歸更快?
- 27. 在所有遷移中都需要gem
- 28. 爲什麼在getOrElse中返回使尾部遞歸不可能?
- 29. 在方案中創建一個尾遞歸功能函數
- 30. Scala列表分區方法的尾遞歸實現
因爲你剛剛說過的話。等待。它不是「Scheme中的所有要求」都是尾遞歸的;要求Scheme能夠正確實現尾遞歸,因爲TR在Scheme中被廣泛使用。而且,因爲沒有它,**'call/cc'將是不可能的。 – 2013-02-19 17:22:52
沒有尾呼的情況下不能有呼叫/呼叫?我不明白你爲什麼這樣說。 – 2013-02-20 18:34:49
@JohnClements我的意思是說「將不可能使用它來適當的TR」。調用'call/cc'應該倒回調用棧;但是如果沒有TR,則對函數的調用會保留其調用堆棧。所以即使你允許它,也會出現堆棧爆炸。這是我的想法。 – 2013-02-22 09:09:35