我有一个学校项目,我应该为Scheme优化编译器/评估器。 任务是尽可能实现尾调用优化。
我知道已知的尾调用优化,如下所示:
(define (f x)
<send some rockets into space>
(f (+ x 1)))
但是,我也在考虑在尾部位置评估操作数。假设如下:
; The function
(define (f a b c)
<send some monkeys into space>
1)
; Call the function with (f 3 4 5)
(f (+ 1 2) (begin (define x 4) x) 5))
评估操作数(+ 1 2)
,(begin (define x 4))
和5
可以在尾部位置进行,对吧?
每个操作数都在自己的环境中进行评估。我尝试使用DrRacket中的常规R5RS,使用以下表达式:
(+ (begin (define x 5) x) x)
如果在同一环境中评估操作数,我将能够将第一个操作数中定义的x
作为第二个操作数传递。但这是不可能的。
那么,我认为每个操作数可以在尾部位置进行评估是否正确?
答案 0 :(得分:4)
“尾部位置”总是相对于某些外部表达。例如,考虑一下:
(define (norm . args)
(define (sum-of-squares sum args)
(if (null? args)
sum
(let ((arg (car args)))
(sum-of-squares (+ sum (* arg arg)) (cdr args)))))
(sqrt (sum-of-squares 0 args)))
对sum-of-squares
的递归调用确实处于相对于sum-of-squares
的尾部位置。但是它相对于norm
处于尾部位置吗?不,因为sum-of-squares
的返回值已发送到sqrt
,而不是直接发送给norm
的来电者。
分析表达式A是否位于相对于外部表达式B的尾部位置的关键是查看A的返回值是否由B直接返回,而不进行任何进一步处理。
在您的情况下,使用您的表达式(f (+ 1 2) (begin (define x 4) x) 5)
(顺便说一下,这实际上并不有效:或许您的意思是(f (+ 1 2) (let () (define x 4) x) 5)
),没有子表达式(+ 1 2)
,{{ 1}}和5位于(let () (define x 4) x)
的尾部位置,因为它们的值必须先收集,然后 传递给f
(是尾调用。)
答案 1 :(得分:2)
应用程序的所有操作数(op1 op2 ...)都处于尾部位置。 对于R5RS Scheme,您可以在此处查看应用程序在尾部上下文中的位置:
https://groups.csail.mit.edu/mac/ftpdir/scheme-reports/r5rs-html.old/r5rs_22.html
答案 2 :(得分:0)
所以我终于明白了。
在常规R6RS中,操作数永远不能在尾部位置进行评估,因为R6RS指定没有严格的顺序来评估它们。
但是,在这个自建的Scheme I评估器中,确定了它们的评估顺序。因此,我可以严格定义哪个运算符是最后一个运算符,并且可以在尾部位置评估该运算符。