爲了演示尾遞歸的有效性,我想要一種方法來訪問Scheme中動態調用堆棧的深度。訪問計劃中的調用堆棧深度
有沒有辦法做到這一點?如果沒有,有沒有辦法在其他主要功能語言(OCaml,Haskell等)中做到這一點?
爲了演示尾遞歸的有效性,我想要一種方法來訪問Scheme中動態調用堆棧的深度。訪問計劃中的調用堆棧深度
有沒有辦法做到這一點?如果沒有,有沒有辦法在其他主要功能語言(OCaml,Haskell等)中做到這一點?
球拍允許您將值存儲在調用堆棧中。 您可以使用它來跟蹤深度。 這裏是我會怎麼做:
#lang racket
;;; This module holds the tools for keeping track of
;;; the current depth.
(module depth racket
(provide (rename-out [depth-app #%app])
current-depth)
(define (extract-current-continuation-marks key)
(continuation-mark-set->list (current-continuation-marks) key))
(define (current-depth)
(car (extract-current-continuation-marks 'depth)))
(define-syntax (depth-app stx)
(syntax-case stx()
[(_depth-app proc args ...)
#'(let ([p (with-continuation-mark 'depth (+ (current-depth) 1)
proc)]
[as (with-continuation-mark 'depth (+ (current-depth) 1)
(list args ...))])
(apply p as))])))
;;; Importing the #%app from the depth module will override
;;; the standard application to use depth-app which keeps
;;; track of the depth.
(require 'depth)
(with-continuation-mark 'depth 0 ; set the initial depth to 0
(let()
;;; Example: foo is tail recursive
(define (foo n)
(displayln (list 'foo n (current-depth)))
(if (< n 5)
(foo (+ n 1))
'done))
;;; bar is not tail recursive
(define (bar n)
(displayln (list 'bar n (current-depth)))
(if (< n 5)
(cons n (bar (+ n 1)))
'()))
;;; Call the examples
(foo 0)
(bar 0)))
輸出是:
(foo 0 2)
(foo 1 2)
(foo 2 2)
(foo 3 2)
(foo 4 2)
(foo 5 2)
(bar 0 2)
(bar 1 3)
(bar 2 4)
(bar 3 5)
(bar 4 6)
(bar 5 7)
'(0 1 2 3 4)
輸出顯示深度不foo
增加,而且它在bar
。
沒有標準的做法。
尾調用優化==沒有調用堆棧增加。通過編寫通常會打擊堆棧並運行的東西來演示它。
深處發出錯誤信號時,你可能會得到一個短的堆棧跟蹤,但它的樣子是特定implentation。
在CL中,您可以跟蹤函數,並且在尾部調用優化時,您將看不到連續尾部調用時的輸出。
由於需要評估,懶惰語言不需要尾遞歸。
非常好。謝謝。 –
嚴格來說,由於Scheme的強制尾部呼叫優化和CPS轉換,這不是一個真正的「呼叫棧」。相反,這會通過強制將調用包裝在前/後代碼中來顯示調用嵌套深度。我並不是100%確定這是如何實現的,但這種代碼很可能會阻止尾遞歸代碼被編譯爲迭代代碼(因爲它會迫使評估返回並在恢復調用深度計數器之後所有的工作都完成了)。 – sjamaan
在這種情況下,「強制執行」是什麼意思? – soegaard