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
相关产品推荐
相关产品推荐

