buffer-local 的疑问

在读叶文彬的 elispIntro, 书中有道练习题无法看懂.

按我自己的理解, 第一次运行时, 第一个 message 输出 This is a variable!, 而第二个 message 应该是输出 I'am local variable!, 因为第二个 message 不是在 let 的范围内的, 应该是 (setq foo I'am local variable!)起作用了. 但是作者给出的答案明显不是如我所想的. 这个答案虽然有,但还是无法理解, elisp 新手求解答…

(set (make-local-variable 'foo) "xxxx") 会让 foo 变成 buffer-local 变量, 并把值赋给它, 而 let 范围内的 setq 赋值的是 buffer-local 变量不是 let 里的 foo, 这就传出了 let 的范围,

而第二个 message 打印的就是这个 foo 的 buffer-local 变量.

应该是这样

这是犄角旮旯里的黑魔法,研究这个感觉没什么意义。(虽然可以迫使你去读 Emacs 源代码……)

第一次运行输出

Making foo buffer-local while locally let-bound!
This is a variable!
This is a variable!

首先 make-local-variable 发现 foo 不是一个 buffer-local variable,于是强行把 foo 这个符号指向一个新创建的 buffer-local variable

(progn
  (setq foo "I'm local variable!")      ; 全局
  (let ((foo "I'm local variable!"))    ; 局部
    ;; 创建 buffer-local variable,覆盖了 let 定义的 foo symbol
    (set (make-local-variable 'foo) "I'm buffer-local variable!")
    ;; 修改的是 buffer-local foo
    (setq foo "This is a variable!")
    (message "%s" foo))
  ;; 读到的是 buffer-local foo
  (message "%s" foo))

第二次运行输出

This is a variable!
I'm local variable!

是因为一个注释过的行为:

If the variable is already arranged to become local when set, this function causes a local value to exist for this buffer, just as setting the variable would do.

也就是代码相当于

(progn
  (setq foo "I'm local variable!")
  (let ((foo "I'm local variable!"))
    ;; (set (make-local-variable 'foo) "I'm buffer-local variable!")
    ;; 相当于
    (set 'foo "I'm buffer-local variable!")
    ;; 上下两句修改的是 let 定义的 foo
    (setq foo "This is a variable!")
    (message "%s" foo))
  ;; 读到的是 buffer-local foo
  (message "%s" foo))
3 个赞

谢谢你如此详细的解释. 我看到你对第一次运营的解释:

(progn
  (setq foo "I'm local variable!")      ; 全局
  (let ((foo "I'm local variable!"))    ; 局部
    ;; 创建 buffer-local variable,覆盖了 let 定义的 foo symbol
    (set (make-local-variable 'foo) "I'm buffer-local variable!")
    ;; 修改的是 buffer-local foo
    (setq foo "This is a variable!")
    (message "%s" foo))
  ;; 读到的是 buffer-local foo
  (message "%s" foo))

中的对第二个 message 的注释, 它读到的也是 buffer-local foo. 是否意味这 let (()) 定义的 foo, 不但被覆盖了, 甚至连 let 的词法作用域都不存在了, 两个 message 的作用域都是只存在于 progn 中了?

谢谢你的解释.

不是,只是 let 引入的这个 binding 不再存在了

(let (a b)
 (setq-local a 123)
 (setq b 321))

这里 b 还是 locally bound 的

谢谢我翻到书本再看了下例子:

有点理解了, 原来想要读到 progn 下的第一个

(setq foo I'am local variable!)

的值.

就必须在 progn 外使用

(with-current-buffer (buffer-name) (message foo))

(在我的电脑上试运行了下)

这是因为第一次运行时, progn 内的 foo 都是 local-variable foo. 而第二次运行时, progn 内除了 let 的作用域外, 也是读取 local-variable foo. 而 let 的作用域内部就只是读取 let local bounding 的 foo.

不知道我的理解是否有误?

我不是正经的程序员, 你用C源码帮我解释, 对我这个指针还看不明白的人来说太高深了, 不过高手能花时间帮我解惑, 我还是很开心的, 谢谢😊