Emacs Lisp: Closure is not a list

复习 Lisp: Syntax and Semantics 继续讨论:

(setq lexical-binding t)

(let ((x 1))
  (setq foo
        '(closure ((x . 1) t) (y)
                  (setq x y)))
  (setq bar
        '(closure ((x . 1) t) ()
                  x)))

(let ((x 1))
  (setq fooo
        (lambda (y)
          (setq x y)))
  (setq baar
        (lambda ()
          x)))

foo
;; => (closure ((x . 1) t) (y) (setq x y))

bar
;; => (closure ((x . 1) t) nil x)

fooo
;; => (closure ((x . 1) t) (y) (setq x y))

baar
;; => (closure ((x . 1) t) nil x)

(funcall foo 2)
;; => 2

foo
;; => (closure ((x . 2) t) (y) (setq x y))

bar
;; => (closure ((x . 1) t) nil x)

(funcall fooo 2)
;; => 2

fooo
;; => (closure ((x . 2) t) (y) (setq x y))

baar
;; => (closure ((x . 2) t) nil x)

我覚得依據上面的代碼,我应該收回這句話。

看來 print 出來的 (closure ...) form 沒有包含 pointer 的引用。Emacs Lisp 的 Closure 并不是列表,它只是能被打印成列表。

However, the fact that the internal structure of a closure is exposed to the rest of the Lisp world is considered an internal implementation detail. For this reason, we recommend against directly examining or altering the structure of closure objects.

https://www.gnu.org/software/emacs/manual/html_node/elisp/Closures.html

应该是 (funcall foo 2) 吧

你这一段例子我没太理解,能不能叙述一下例子的目的 :joy:

我猜测

(closure ...)

就是闭包的字面表述,虽然看起来像列表,但却需要整体来看, 就好比: >= 不能拆开来理解一样

但这样理解还是有疑问, 如果是作为整体来看,那么下面的例子应该是可求值的:

(closure ((x . 1) t) (y)
     (setq x y))

但实际对其进行求值, 是按 函数调用的规则来求值,并且结果报错, 所以从这个角度来说, lisp 解释器似乎没有设立规则来专门处理它。

 '(closure ((x . 1) t) (y)
              (setq x y))

如果对其引用, 那么根据我的了解, 这个东西应该按照不可变的常量来处理, 在lisp 内部,确实保存为 闭包对象。

(setq lexical-binding t)

(setq a '(closure ((x . 1) t) (y)
                  (setq x y)))

(let ((x 1))
  (setq b (lambda (y)
            (setq x y))))

(functionp a) => t
(functionp b) => t

(eq a b) => nil
(equal a b) => t

我脑袋抽了, 字面表述转换为内部对象,是 lisp 读取器的工作, 求值是解释器的工作,不能混为一谈。

那新的问题就是: (closure xxx) 经过lisp读取器后,得到的内部语法树到底是什么? 是一个list object? 还是闭包object? 还是其他?

UPDATE: 还真是 list object:

(setq lexical-binding t)

(let ((x 1))
  (setq b (lambda (y)
            (setq x y))))

(functionp b) => t

(listp b) => t
(let (x)
  (defun foo (y)
    (setq x y))
  (defun bar ()
    x))
bar

(foo 2)
2
(bar)
2

主干,利用 let over lambda 在 closure 之间 share 变量,上述例子中 foo bar 访问的 x 是同一个地址。用 foo 设置一个值,bar 会返回同一個值。

而 Topic 中的例子证明用 (lambda ...) 建立的 closure 和直接用 (closure ...) 建立的 closure 不是在任何時候都是等价的

但却需要按上下文(context) 来看。

equal test 对 function 來说沒有实际意义。

(closure ...) 能直接用 funcall 处理估计是为了实現方便。闭包object 被當作 list 估计也是为了实現方便。 我之前己证明了在內部 闭包object 不是 list。

你又忘了 byte-compile

哈哈, 很少用这个东西,所以想不起来 :joy:

其实这个 quote 的 闭包列表 听有意思, 值得探讨探讨, 它到底是什么时候创建了一个闭包对象的呢? quote 的行为有时候也是不太好懂

quote 是正常的,但是 byte-compilefuncallfset 之類內部应該会对 (closure ..) 特別处理。

确实 funcall 的代码中可以看出来:

  if (EQ (funcar, Qlambda)
	  || EQ (funcar, Qclosure))
	val = funcall_lambda (fun, numargs, args + 1);
1 个赞