1

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

(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 演示)。

4

2 回答 2

2

我看不出如何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.

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

我们需要打电话trampoline。根据trampoline上面的定义,我们绑定f...并返回(lambda args (let ((result (apply f args))) ...))

trampoline我们从f.

我们现在需要评估(应用to和((lambda args (let ((result (apply f args))) ...)) n 1)的返回值)。trampolinen1

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是一个参数)。但是那个调用已经返回并且绑定被移除了,所以f是未绑定的。


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

它像预期的那样爆炸:因为Can't use an undefined value as a subroutine reference这条线是未绑定的。local $result = $f->(@args);$f

如果将所有绑定更改为词法(替换所有出现的localby my),则按预期$fac->(5)返回。120

于 2018-09-14T22:03:25.373 回答
2

不可以。 Trampoline 和 Y 组合器使用闭包。

动态范围没有闭包,因此引用自由变量的过程/函数意味着程序调用堆栈中具有该名称的任何变量。

在词法范围中,它是创建 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将值存储20test,而在动态范围内test是绑定最接近的值,因此它是局部变量10。还有打电话的时候:

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

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

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

编辑

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

于 2018-09-15T01:53:28.020 回答