Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/git/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
公共Lisp和Scheme词法闭包的区别_Lisp_Scheme_Common Lisp_Let_Lexical Closures - Fatal编程技术网

公共Lisp和Scheme词法闭包的区别

公共Lisp和Scheme词法闭包的区别,lisp,scheme,common-lisp,let,lexical-closures,Lisp,Scheme,Common Lisp,Let,Lexical Closures,在Common Lisp中,我可以计算以下代码片段(在SBCL中),而不会显示任何语法错误: (let ((x 0)) (defun my-incf (y) (setf x (+ x y))) (defun my-decf (y) (setf x (- x y)))) MY-DECF CL-USER> (my-incf 1) 1 CL-USER> (my-incf 1) 2 CL-USER> (my-decf 1) 1 CL-USER>

在Common Lisp中,我可以计算以下代码片段(在SBCL中),而不会显示任何语法错误:

(let ((x 0))
   (defun my-incf (y)
     (setf x (+ x y)))
   (defun my-decf (y)
     (setf x (- x y))))
MY-DECF

CL-USER> (my-incf 1)
1
CL-USER> (my-incf 1)
2
CL-USER> (my-decf 1)
1
CL-USER> (my-decf 1)
0
当我尝试计算对应的Scheme代码片段(在DrRacket中)时:

它表示语法错误

begin (possibly implicit): no expression after a sequence of internal definitions in: (begin (define (my-incf y) (set! x (+ x y))) (define (my-decf y) (set! x (- x y))))

有人知道为什么不能在Scheme中执行此操作吗?

您不能在Scheme中的顶级绑定之外定义顶级绑定。(在
的内部,let
肯定在顶层之外——相反,您拥有的是内部定义,而不是导出到顶层。)但是,使用
定义值
,您仍然可以执行需要执行的操作:

(define-values (my-incf my-decf)
  (let ((x 0))
    (values (lambda (y)
              (set! x (+ x y))
              x)
            (lambda (y)
              (set! x (- x y))
              x))))
但是,您仍然可以使用内部定义,以使代码更具可读性:

(define-values (my-incf my-decf)
  (let ((x 0))
    (define (my-incf y)
      (set! x (+ x y))
      x)
    (define (my-decf y)
      (set! x (- x y))
      x)
    (values my-incf my-decf)))

两全其美。:-)在本例中,
将内部的
我的incf
我的decf
定义发送到外部的
定义值
,这是真正的顶级定义发生的地方。

Chris的解决方案是我在本例中使用的,但这是另一个本着“让Lambda过去”的精神的解决方案如果增加x上的操作数,这将非常有用:

(define inc-dec
  (let ((x 0))
    (lambda (msg y)
      (case msg
        ((incf) (set! x (+ x y)))
        ((decf) (set! x (- x y)))
        (else (error "wot?")))
      x)))


(inc-dec 'incf 1)
(inc-dec 'incf 1)
(inc-dec 'decf 1)
(inc-dec 'decf 1)

不那么优雅,但非常简单:定义顶级变量,然后
set
setf
将它们从
let
内部发送到lambdas,这取决于它是Scheme还是CL.

您需要查看let-in-DrRacket的语法定义。公共Lisp示例是有效的,但我不会在代码中使用它,而是使用CLOS。使用上述方法会使调试更加困难,并且会防止编译器将DEFUN表单识别为函数定义。在里面让德芬不再是一个顶级的形式。这不是很明显吗?X是CLOS实例中的一个插槽。那么函数就是方法。顺便说一句,关于你所做的与你可以用CLOS做的相似之处,Scheme最初是作为一种探索面向对象思想的语言而发明的——大概是使用类似于下面描述的技术。我不一定使用OOP来解决带有全局变量的模块的简单问题。函数不是顶级表单的问题可以通过
(defvar*counter*0)
解决,并让函数参考
*counter*
。如果模块尖叫“我真的应该是一个支持多个实例化的对象,并且所有这些全局变量都应该是我的实例变量”,那么OOP方法适用。如果您没有预料到需要多个实例化,或者继承带来的任何好处,那么请坚持使用globals。动态变量是处理全局变量的“通用Lispy”方法。正如我曾经对我说过的一位老师所说:“Scheme是一种现代Lisp”。
(define inc-dec
  (let ((x 0))
    (lambda (msg y)
      (case msg
        ((incf) (set! x (+ x y)))
        ((decf) (set! x (- x y)))
        (else (error "wot?")))
      x)))


(inc-dec 'incf 1)
(inc-dec 'incf 1)
(inc-dec 'decf 1)
(inc-dec 'decf 1)