Elisp和CommonLisp的闭包的区别


#1

Elisp的闭包和CommonLisp的闭包好像不太一样,现在在电车上,没法验证。先写在这里,免得明天忘了。

Elisp的闭包好像是直接把引用的值放在自己内部。而CommonLisp里是一个地址?

CommonLisp的闭包输出是这样的

(let ((count 0))
  (list
   #'(lambda () (incf count))
   #'(lambda () (decf count))
   #'(lambda () count)))
;; =>(#<CLOSURE (LAMBDA ()) {10033FCA7B}> 
;;    #<CLOSURE (LAMBDA ()) {10033FCA9B}>
;;    #<CLOSURE (LAMBDA ()) {10033FCABB}>)

也就是,三个闭包共用同一个变量

我记得Elisp的闭包输出是这样的形式

(closure  ((count . 0) t) nil (lambda () …))

这是不是,Elisp闭包的实现是把引用的变量存在自己内部一份?

上面的代码在Elisp中,三个闭包是不是共用同一个变量?哪位现在方便的话,帮我验证一下就更好了 :grin:


#2

有词法作用域才有闭包,所以上面代码用elisp写就不是闭包,需要使用lexical-let


#3

我的意思是启动词法作用域,不然我上面举的例子,就不是下面这种格式了。

(closure  ((count . 0) t) nil (lambda () …))

而应该是这种格式了

(lambda () …) 

#4

看起来是而已

(let ((a (list 1 2 3))) (setq b a fn (lambda () a)))
(cl-assert (eq (eq b (funcall fn)) t))

#5

听起来common lisp使用了引用捕捉(capture by reference),而elisp使用了值捕捉(capture by value)

lambda的词法作用域确实有这两种实现。

比如:Java就是值捕捉。

因此,如果你想在elisp里也实现引用捕捉,方法就是用另一个对象把它包起来(i.e: Box它),这种手段在Java的lambda里也经常使用,比如用一个数组把对象包起来 [你的对象]


#6

都说了看起来是,你跑跑我上面的代码就知道了,eq可是只有引用相等的时候才能返回t的。(把list换成hash-table之流也一样)

你问为啥这么诡异?要序列化的嘛


#7

你在回复我吗?我在提醒列表里收到了Notification。。。

这鬼论坛的回复机制有问题:

只回一楼或楼上不提示,但如果一楼和楼上不是同一个人的时候,无法区分到底回复的是谁。。。


#8

是回复你,紫薯补丁

三楼是回复楼主的。

我在5楼反驳你是因为你觉得Elisp是值捕获,然而人家是正版的引用捕获。

你解答错了。那一句算是我自问自答


#9

那你3楼是回复谁的?

这论坛我真看不懂。。。

我不清楚你这个代码想描述什么?

另外还有个问题,就是:楼主说Elisp和Common Lisp的实现有区别。

这里Elisp是跑Emacs里的(Emacs本身就是运行时环境),而Common Lisp是另一个语言,应该在别的运行时环境里(Emacs只是作为编辑器)。但是ELisp又支持CL宏,这导致问题复杂化。

你让我跑这段代码,到底是要我在Emacs里跑,还是在CL的专有环境里跑?

楼主的情况也类似。


#10

我只是说听起来。。。

这么说的话Elisp也是引用捕获。

换句话说是楼主的实验结果有问题。。。


#11

不是,你大概不懂啥叫序列化。

我之前做了点实验

说明一个闭包打印/序列化成 (closure ...) 是损失了额外的信息的。


#12

你还不知道 Emacs 24 开始就有了 (setq lexical-binding t) 吧。


#13

我知道有,但默认不开。


#14

刚才测试了一下,你的这个实验在ELisp上确实没Crash。

但这并不能说明ELisp就是capture by reference,因为如果是capture by value的话,结果也是一样的。

注意:a的值实际上是list的地址,即使是capture by value,它照样获得list的地址。所以打印出来地址一样并不能说明什么。

以下证明Elisp是capture by reference

(setq lexical-binding t)
(let ((x "1" ))
  (progn
    (message x)
    ((lambda () (setq x "2" )))
    (message x)))

如果是Java的话(capture by value),则对局部lambda里面的那个x修改,不会影响到外面。


#15

有图有真相

hello

#1 才是关键所在


#16

Capture by value 就不会有 eq 这玩意了


#17

好的,#1说明是capture by reference

到公司我就不用再验证了


#18

歪楼请教个问题

里面的 “一只满嘴名言的牛” 有一句

A LISP programmer knows the value of everything, but the cost of nothing.

该如何理解这句话。


#19

不知道,因为 LISP 是五十多年前的事了,不是当时的硬件就没有什么实感。老夫都没到五十岁。


#20

现在应该是60多年前了吧

http://wiki.c2.com/?LispLanguage