如何在Racket中实现call-stack-max-depth函数以获取无参函数的最大调用栈深度
Calculating Maximum Call Stack Depth in Scheme Using Continuation Marks
Got it, let's tackle writing that call-stack-max-depth function you need. The core idea is to use continuation marks to track the current stack depth as we execute the target thunk, while keeping a running tally of the maximum depth reached throughout the execution.
Here's a working implementation (Racket-specific, adaptable to other Scheme variants with similar continuation mark support):
#lang racket (define (call-stack-max-depth thunk) ; Create a unique key to track stack depth in continuation marks (define depth-key (make-continuation-mark-key 'call-stack-depth)) ; Initialize our max depth tracker (define max-depth 0) ; Wrapped apply function that updates depth and tracks the maximum value (define (traced-call proc . args) ; Calculate current depth: parent depth + 1 (default to 0 if no parent mark exists) (define current-depth (add1 (or (continuation-mark-set-first #f depth-key) 0))) ; Update max depth if current depth is larger (when (> current-depth max-depth) (set! max-depth current-depth)) ; Set the new depth mark and execute the original procedure (with-continuation-mark depth-key current-depth (apply proc args))) ; Temporarily override the default apply behavior to use our traced version (parameterize ([current-apply traced-call]) (thunk) ; Run the target thunk max-depth)) ; Return the recorded maximum stack depth
How this works:
- Continuation Mark Key: We create a unique key (
depth-key) to store the current stack depth in the continuation mark set. This ensures our tracking doesn't interfere with other system-level marks. - Traced Apply: The
traced-callfunction replaces the defaultapplybehavior. Every time a procedure is called, it increments the parent context's depth to get the current depth, updates the maximum depth if needed, sets the new depth mark, then executes the original procedure. - Parameterize: Using
parameterizelets us safely overridecurrent-applyonly for the duration of running the thunk—no global changes, so other code isn't affected.
Testing with your factorial examples:
Let's verify the function behaves as expected:
; Recursive factorial (should have a stack depth ~101 for n=100) (define (fac n) (if (<= n 1) 1 (* n (fac (- n 1))))) (call-stack-max-depth (lambda () (fac 100))) ; Returns ~101 (varies slightly by implementation) ; Iterative/tail-recursive factorial (constant small stack depth) (define (fac-iter n) (define (iter acc count) (if (<= count 0) acc (iter (* acc count) (- count 1)))) (iter 1 n)) (call-stack-max-depth (lambda () (fac-iter 100))) ; Returns a small constant (e.g., 3 or 4)
Notes for other Scheme implementations:
If you're not using Racket, adjust the procedure call interception step to match your Scheme's capabilities:
- Chez Scheme uses
$applyinstead ofcurrent-apply - Some variants may require
make-procedure-wrapperor other extension hooks - The core logic of tracking depth via continuation marks stays consistent—you just need to hook into your implementation's procedure call mechanism.
内容的提问来源于stack exchange,提问作者TomatoFarmer
相关产品推荐
相关产品推荐

