在 Scheme 中访问调用堆栈深度

Accessing call stack depth in Scheme

为了演示尾递归的有效性,我想要一种在Scheme中动态访问调用堆栈深度的方法。

有办法吗?如果没有,有没有办法在其他主要函数式语言(OCaml、Haskell 等)中做到这一点?

没有标准的方法来做到这一点。

尾调用优化== 没有增加调用堆栈。您通过编写通常会破坏堆栈和 运行 的内容来演示它。

在发出深层错误信号时,您可能会得到一个简短的堆栈跟踪,但它的外观是特定于实现的。

在 CL 中,您可以跟踪函数,并且在尾调用优化后您将看不到连续尾调用的输出。

惰性语言不需要尾递归,因为评估是按需进行的。

Racket 允许您在调用堆栈中存储值。 您可以使用它来跟踪深度。 以下是我的做法:

#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 中增加。