带有蹦床和 Y 组合器的代码是否应该在具有动态范围的 lisp 中工作?

Should code with trampoline and Y combinator work in lisp with dynamic scope?

我在 javascript 中有 lisp,它类似于 scheme。它可以与词法和动态范围一起使用。我不确定动态作用域是如何工作的,看起来没问题,但是当作用域是动态的时,这段代码不起作用:

(define Y
    (lambda (h)
       ((lambda (x) (x x))
           (lambda (g)
              (h (lambda args (apply (g g) args)))))))

(define (trampoline f)
    (lambda args
       (let ((result (apply f args)))
           (while (eq? (type result) "function")
               (set result (result)))
            result)))

(define (! n)
     ((trampoline (Y (lambda (f)
          (lambda (n acc)
             (if (== n 0)
                 acc
                 (lambda ()
                     (f (- n 1) (* n acc)))))))) n 1))

(print (! 1000))

当范围是词法时,它工作正常。当范围是动态的时,这段代码应该工作吗?现在它什么都不做,我不知道为什么,但我想确保这段代码在我开始调试之前能正常工作,并因此使我的动态范围中断。

我的带演示的 lisp 在这里 https://jcubic.github.io/lips/ 但是使它在词法范围内工作的代码尚未发布,因此它不会工作。 (它在 devel 分支中,我可以用它或使用 Stack Snippet 创建 codepen 演示)。

我不明白 trampoline 如何使用动态范围。

简化评估:

(define Y ...)

现在 Y 被绑定(到某个值)。

(define (trampoline f)
    (lambda args
       (let ((result (apply f args)))
           ...)))

现在 trampoline 绑定到 (lambda (f) (lambda args (let ((result (apply f args))) ...)))

(define (! n)
     ((trampoline ...) n 1))

现在 ! 绑定到 (lambda (n) ((trampoline ...) n 1))

(print (! 1000))

我们首先评估内部调用,因此我们需要解析!并将其应用到1000

根据上面 ! 的定义,我们将 n 绑定到 1000 并评估 ((trampoline ...) n 1).

我们需要打电话给 trampoline。根据上面 trampoline 的定义,我们将 f 绑定到 ... 和 return (lambda args (let ((result (apply f args))) ...)).

我们从trampolinereturn解开f的绑定.

我们现在需要评估 ((lambda args (let ((result (apply f args))) ...)) n 1)(将 trampoline 的 return 值应用于 n1)。

n 当前绑定到 1000,因此此表达式变为 ((lambda args (let ((result (apply f args))) ...)) 1000 1)。为了执行调用,我们将 args 绑定到 (1000 1).

现在我们需要计算 (apply f args)(将结果绑定到 result 作为 let 的一部分)。 apply 在标准库中。 args 刚刚绑定到上面的 (1000 1)。但是 f.

没有绑定

在这一点上我们应该抛出一个错误:到目前为止我们看到的唯一绑定 f 是在调用 trampoline 期间(其中 f 是一个参数) .但是那个调用已经 returned 并且绑定被删除了,所以 f 是未绑定的。


现场演示(使用您的代码的 Perl 版本,其中所有绑定都是手动动态化的):https://ideone.com/DWjwBj

正如预测的那样爆炸了:Can't use an undefined value as a subroutine referencelocal $result = $f->(@args); 因为 $f 是未绑定的。

如果您将所有绑定更改为词法绑定(将所有出现的 local 替换为 my),$fac->(5) returns 120 将按预期进行。

没有。 Trampoline 和 Y 组合器与闭包一起使用。

动态作用域没有闭包,因此引用自由变量的 procedure/function 表示程序调用堆栈中具有该名称的任何变量。

在词法范围内,它是创建lambda时捕获的变量。因此代码:

(define test 10)

(define (make-adder test)
  (lambda (v) (+ test v)))

(define add20 (make-adder 20))

(add20 5) 
; ==> 25 in lexical scope
; ==> 15 in dynamic scope 

原因很简单。 make-adder 返回的函数将值 20 存储为 test,而在动态范围内 test 是最接近的绑定,因此它是局部变量 10。同样在调用时:

(let ((test 30))
  (add20 5))
; ==> 25 in lexical scope
; ==> 35 in dynamic scope

现在 Common Lisp 有了动态作用域和词法作用域。动态作用域变量是用 defvardefparameter 定义的顶级变量或声明为特殊变量。这很容易出错,因此我们使用 *earmuffs* 为此类变量专门命名。

Scheme 的参数是可变对象,并且有更新和恢复它的语法,以便它充当动态变量。

编辑

我已经测试了你的词汇和动态 lisp,两者似乎都能按预期工作。