You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Emacs Lisp中评估表单序列为何出现不同结果?

问题描述

将以下Elisp代码粘贴到Emacs scratch缓冲区:

; Source - https://stackoverflow.com/a/28991218
; Posted by quicoju
; Retrieved 2026-02-24, License - CC BY-SA 3.0

(setq the-empty-stream nil)
(defun stream-null? (s) (eq s the-empty-stream))
(defun my/sicp-delay (exp) `(lambda () ,exp))
(defun my/sicp-force (exp) (funcall exp))

(defmacro my/cons-stream (a b) (list 'cons a (my/sicp-delay b)))
(defun my/stream-car (stream) (car stream))
(defun my/stream-cdr (stream) (my/sicp-force (cdr stream)))

(defun my/stream-enumerate-interval (low high)
  (let ((l low)
                (h high))
    (if (> l h)
        the-empty-stream
      (my/cons-stream low
                      (my/stream-enumerate-interval (1+ l) h)))))

使用M-x eval-buffer评估整个缓冲区后,执行表达式:

(my/stream-car (my/stream-cdr (my/stream-cdr (my/stream-enumerate-interval 10 20))))

会报错:

Debugger entered--Lisp error: (void-variable l)
  (1+ l)
  (my/stream-enumerate-interval (1+ l) h)
  .
  .
  .

但如果手动逐个用C-M-x评估每个表单后执行该表达式,能得到正确结果12。这是什么原因?

原因分析

核心问题出在**my/sicp-delay的定义类型和Emacs Lisp编译/解释模式的作用域差异**:

  1. SICP中实现流的delay必须是宏,因为它需要捕获未求值的表达式,避免立即执行。但你的代码里my/sicp-delay被定义成了函数——函数的参数会被立即求值,这本身就不符合流的延迟语义,只是在解释模式下出现了特殊的可运行状态。

  2. 当用M-x eval-buffer时,整个缓冲区会被编译为字节码:

    • 宏my/cons-stream会被提前展开,my/stream-enumerate-interval里的代码会变成(cons low (my/sicp-delay (my/stream-enumerate-interval (1+ l) h)))。
    • 编译器处理这段代码时,会识别出l和h是let绑定的词法局部变量,而my/sicp-delay返回的lambda是延迟执行的——当后续my/sicp-force调用这个lambda时,原let的作用域早已消失,编译后的字节码无法捕获这些词法变量,因此运行时会触发void-variable l的错误。
  3. 当用C-M-x逐个评估表单时,代码以解释模式运行:

    • Emacs Lisp解释模式默认使用动态作用域,lambda里的l和h会在动态作用域链中查找。
    • 加上解释模式对函数参数和反引号的处理更宽松,(my/stream-enumerate-interval (1+ l) h)没有被立即求值,而是被保留为表达式插入到lambda中。当my/sicp-force调用lambda时,动态作用域中还能找到l和h的绑定,因此能正确执行并返回结果12。
解决方法

把my/sicp-delay从函数改为宏,这样就能正确捕获未求值的表达式,无论编译还是解释模式都能正常工作:

(defmacro my/sicp-delay (exp) `(lambda () ,exp))

内容的提问来源于stack exchange,提问作者wejaeger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 12:45:54