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

Lisp中(mymacro arg)与(eval (myfunc (quote arg)))语义差异示例求证

宏与eval(fun(quote arg))的语义差异示例

我们可以通过局部变量作用域的差异构造核心反例,本质原因是:宏展开后的代码会在宏调用的原始环境中求值,而eval执行的代码只能访问全局环境或当前动态环境,无法继承调用eval时的局部变量。

示例代码

先定义宏和对应的函数:

;; 宏:展开后的代码引用调用环境中的变量y
(defmacro mymacro (param)
  `(+ ,param y))

;; 函数:与宏体完全相同,返回相同的S表达式
(defun myfun (param)
  `(+ ,param y))

测试场景1:局部变量环境中的调用

;; 调用宏:展开后在局部环境求值
(let ((y 5))
  (mymacro 3))  ;; 结果为8

宏展开后的代码是(+ 3 y),该代码在let ((y 5))的局部环境中执行,y取值为5,最终计算得3+5=8。

;; 调用eval(myfun(quote arg)):在全局环境求值
(let ((y 5))
  (eval (myfun '3)))  ;; 结果取决于全局变量y,未定义则报错

(myfun '3)返回S表达式(+ 3 y),eval执行该表达式时,会在全局环境中查找y的值。若全局未定义y,会直接抛出未绑定变量的错误;若全局y为10,结果就是13,和宏调用的8完全不同。

另一个关键差异:执行时机

再看一个涉及副作用的例子,直观展示宏的展开时机特性:

(defmacro mymacro (param)
  (format t "宏正在展开~%")
  `(progn
     (format t "执行展开后的代码~%")
     ,param))

(defun myfun (param)
  (format t "函数正在运行~%")
  `(progn
     (format t "执行展开后的代码~%")
     ,param))

调用宏的输出顺序:

(mymacro (format t "参数正在求值~%"))
;; 输出:
;; 宏正在展开
;; 执行展开后的代码
;; 参数正在求值

调用eval(myfun(...))的输出顺序:

(eval (myfun '(format t "参数正在求值~%")))
;; 输出:
;; 函数正在运行
;; 执行展开后的代码
;; 参数正在求值

宏的展开逻辑在代码求值前执行,而函数的逻辑在调用函数时执行,两者执行时机的差异是宏与函数的核心语义区别之一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 08:56:23