带有蹦床和 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))) ...))
.
我们从trampoline
return解开f
的绑定.
我们现在需要评估 ((lambda args (let ((result (apply f args))) ...)) n 1)
(将 trampoline
的 return 值应用于 n
和 1
)。
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 reference
行 local $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 有了动态作用域和词法作用域。动态作用域变量是用 defvar
、defparameter
定义的顶级变量或声明为特殊变量。这很容易出错,因此我们使用 *earmuffs*
为此类变量专门命名。
Scheme 的参数是可变对象,并且有更新和恢复它的语法,以便它充当动态变量。
编辑
我已经测试了你的词汇和动态 lisp,两者似乎都能按预期工作。
我在 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))) ...))
.
我们从trampoline
return解开f
的绑定.
我们现在需要评估 ((lambda args (let ((result (apply f args))) ...)) n 1)
(将 trampoline
的 return 值应用于 n
和 1
)。
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 reference
行 local $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 有了动态作用域和词法作用域。动态作用域变量是用 defvar
、defparameter
定义的顶级变量或声明为特殊变量。这很容易出错,因此我们使用 *earmuffs*
为此类变量专门命名。
Scheme 的参数是可变对象,并且有更新和恢复它的语法,以便它充当动态变量。
编辑
我已经测试了你的词汇和动态 lisp,两者似乎都能按预期工作。