在tailposition中编译/评估操作数

时间:2014-08-03 11:14:23

标签: scheme evaluation tail-recursion

我有一个学校项目,我应该为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作为第二个操作数传递。但这是不可能的。

那么,我认为每个操作数可以在尾部位置进行评估是否正确?

3 个答案:

答案 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评估器中,确定了它们的评估顺序。因此,我可以严格定义哪个运算符是最后一个运算符,并且可以在尾部位置评估该运算符。