Common Lisp结构体字面量在函数与宏定义中的使用问题
Common Lisp random-state 构造问题解答
错误原因梳理
你遇到的两个问题本质上都和 Common Lisp 的执行阶段逻辑有关:
#.是读取器宏,会在代码被Lisp读取器解析的阶段就执行求值,远早于函数参数绑定的运行阶段,所以函数定义时读取器执行#.(make-array ...)时,some-int还完全不存在,自然会报未定义。- 反引号的逗号替换是在宏展开阶段处理的,而
#S结构字面量是读取阶段就被解析为结构对象的,读取器看到#S(就会直接把后面的内容解析成结构,根本不会识别里面的逗号语法,所以会报「反引号结构内的逗号不属于列表或通用向量」的错误。
问题1:函数silly-random-state实现
你自己写的read-from-string方案确实可用,但性能较差且存在输入注入风险,更优雅的原生实现直接调用SBCL的random-state构造接口即可,最终生成的对象和字面量构造结果完全一致:
(defun silly-random-state (some-int) (check-type some-int (unsigned-byte 32)) (let ((state-array (make-array 627 :element-type '(unsigned-byte 32) :initial-element some-int))) (sb-kernel:make-random-state :state state-array)))
如果需要兼容SBCL以外的实现,可以保留read-from-string的写法,只要补充输入校验避免注入风险即可。
问题2:宏silly-random-state-macro实现
宏的实现只需要把逗号的替换逻辑移到读取器处理#S之前即可,直接在宏函数体内完成数组和结构的构造,再把构造好的random-state对象放到展开结果里:
(defmacro silly-random-state-macro (some-int) (let ((int-val (eval some-int))) (check-type int-val (unsigned-byte 32)) (let ((state-array (make-array 627 :element-type '(unsigned-byte 32) :initial-element int-val))) `(quote ,(sb-kernel:make-random-state :state state-array)))))
如果你希望宏展开为运行时计算的代码,而不是直接嵌入常量对象,更适合参数是动态变量的场景,可以写成下面的形式:
(defmacro silly-random-state-macro (some-int) `(let ((state-array (make-array 627 :element-type '(unsigned-byte 32) :initial-element ,some-int))) (sb-kernel:make-random-state :state state-array)))
内容的提问来源于stack exchange,提问作者kjo
相关产品推荐
相关产品推荐

