Elisp和CommonLisp的闭包的区别

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

我知道有,但默认不开。

刚才测试了一下,你的这个实验在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修改,不会影响到外面。

1 个赞

有图有真相

hello

#1 才是关键所在

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

好的,#1说明是capture by reference

到公司我就不用再验证了

歪楼请教个问题

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

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

该如何理解这句话。

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

现在应该是60多年前了吧

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

序列化,基本上所有语言都有。你这里指的是list或object转化为可打印形式,还是指其它的什么?

你的实验倒是和我昨晚留作今天要确认的问题相关

我现在正在看Practical Common lisp,每天能抽出来的时间不多,就一天一章的消化吧。好在一章内容才十几页 :joy:

指的是 closure 转化为可打印形式,我的意思是打印出来的 (closure ...) 并不代表 closure 的实际结构,至少引用是丢失了的。

Common Lisp 里任何物件都有地址,closure 自然也有。打印成 ,#<TYPE ADDRESS> 是所有没有设置如何打印的物件的模式行为,只要设置 pprint dispatch 就算把 clsoure 物件打印成 "fuc*" 都可以。

为什么Capture by value 就不需要eq ?

比如:Java就是Capture by value,但它还是有 == 。

eq 有点特殊

他会认为 1 == 1 但 #xFFFFFFFFFFFFFFFF != #xFFFFFFFFFFFFFFFF

他会认为 ?c == ?c 但 “hello” != “hello”

而且 (eq '(1) '(1)) 返回的居然是nil 但是 '() == '()

(eq nil '()) ;; => t

你的试验结果正印证了我的观点:

eq一点不特殊,即:

如果比较的对象是数值,则直接比较两个数值;如果比较对象是object,则比较他们的地址。

(eq 1 1) ; => t

因为1是数值

(eq #xFFFFFFFF #xFFFFFFFF) ; => t

同理,#xFFFFFFFF是数值

(eq #xFFFFFFFFFFFFFFFF #xFFFFFFFFFFFFFFFF) ; => nil

虽然#xFFFFFFFFFFFFFFFF)是数值,但是超过了整数的最大位数32BIT,所以底层变成了一个object数据结构,于是比较的是地址

(eq "hello" "hello") ;; => nil

因为string在ELisp里被看成一个object,于是比较的是地址。

注意:这与Java的String对象很类似,只不过在Elisp里这个string不会被intern,而Java的String字面量会intern,于是Java可以直接对String字面量进行==比较,而Elisp不行。如果你想用和Java那样的intern的string,请使用(eq 'hello 'hello) ; => t

(eq '(1) '(1)) ; => nil

这是因为'(1)是一个list,它是object,所以比较的是地址

(eq '() '()) ; => t

这是因为'()空列表在Elisp里被看成和Java null一样的东西,显然 null == null

你错了,eq 永远比较的是地址,只是 Lisp 中为了效率实现成同样数字(整数,浮点数)通常是同个地址罢了。如果是早期用 Church encoding 表示数字的实现 eq 两个相同数值就会是 nil 了。Common Lisp 标准对 eq 两个数值没有任何保证。

== 很明显是个重载运算符,eq 不是。对于一个 capture by value,比较地址自然没有意义。至于 Emacs 的 char 为什么也能 eq,是因为 char 实际上就是个整数。

1 个赞

这也不能算错吧。。。我这个只是从实现层面解释(语义层确实可以把他们全部理解为引用)

但这和是否需要eq并没有关系。

比方说 这段ELisp代码:

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

这里的lambda捕获的了x,我完全可以把Elisp的closure设计成和Java那样,new一个struct把x的值包到对象里面,于是就有了capture by value。

但这并不表示我这么做了之后eq就没用了,eq的使用方式和现在并没有区别。

我一开始没有看明白值捕捉是啥,看 @cireu 的以为是传值传引用顺口说了。

然而你开口就什么 Elisp 是值捕捉,eq 是数值比较。

值捕捉只要改下 lambda 宏自动把 free variable 加进 &aux arglist 就能实现了。

我也没整懂,当时学java的时候没学fp相关的。后来也没了解java的闭包是值捕捉这种太监闭包

反正无副作用下语义一样。