如何防止lisp宏中的表单评估?

如何防止lisp宏中的表单评估?,第1张

概述我正在尝试创建一个简单的备忘录.如何防止在此代码中评估args表单? (defmacro defun/memo (name args &rest body) `(let ((memo (make-hash-table :test 'equalp))) (defun ,name ,args (if (gethash (loop for x in ,args collect 我正在尝试创建一个简单的备忘录.如何防止在此代码中评估args表单?

(defmacro defun/memo (name args &rest body)  `(let ((memo (make-hash-table :test 'equalp)))     (defun,name,args       (if (gethash (loop for x in,args collect x) memo)           (gethash (loop for x in,args collect x) memo)           (let ((result (progn,@body)))             (setf (gethash (loop for x in,args collect x) memo) result)             result)))))

错误:

; in: DEFUN ADD;     (X Y); ; caught STYLE-WARNING:;   undefined function: X; ; compilation unit finished;   Undefined function:;     X
解决方法
(defmacro defun/memo (name args &rest body)

你通常用身体和身体来宣称身体,而不是身体.

变量捕获

`(let ((memo (make-hash-table :test 'equalp)))

备忘录符号将以生成的代码结束.如果body包含对memo的引用,例如在defun / memo调用之外被词法绑定的符号,那么它将使用你的变量.您应该使用一个新的符号,在宏内部生成gensym(在反引号之外).例如,您可以执行以下 *** 作以避免两次评估expr:

(let ((var-expr (gensym)))  `(let ((,var-expr,expr))     (+,var-expr)))

(if (gethash (loop for x in,args collect x) memo) result)             result)))))

以下应该做什么?

(loop for x in,args collect x)

假设您使用(defun / memo test(a b c)…)定义一个函数,您将在上面注入参数的文字列表,这将导致代码包含:

(loop for x in (a b c) collect x)

如您所见,代码现在尝试使用参数b和c调用函数a.

如果你在你的宏中引用了args怎么办?

(loop for x in ',args collect x)

然后,您将获得:

(loop for x in '(a b c) collect x)

现在,您只是复制一个文字列表.当运行上面生成的代码时,它只会构建一个新的列表(a b c).这就是你需要的吗?

你想要的是获取你的函数的所有参数,即你给出的值列表.循环可以替换为:

(List,@args)

哪个会扩展为:

(List a b c)

在这里,您将列出所有值.

但Common lisp已经提供了一种将所有参数作为列表获取的方法:

(defun foo (&rest args)   ;; args is bound to a List of values)

您生成的函数也可以这样做.

Gethash

此外,(if(gethash …)(gethash …)其他)可以写(或(gethash …)其他).这样做的好处是只能对gethash调用一次.

更重要的是(感谢@Sylwester),因为你正在编写一个通用宏,你不能事先知道nil是否可能是返回值.考虑到if /或是如何编写的,具有nil值将使结果每次重新计算.您需要使用gethash中的辅助返回值来检查元素是否存在:

(multiple-value-bind (value exists-p) (gethash ...)  (if exists-p      value      (setf (gethash ...) ...)))

此外,如果您的缓存函数返回多个值,您可能希望使用multiple-value-List获取它们并使用values-List返回它们.

SETF

顺便说一句,代码如下:

(let ((result expr))  (setf place result)  result)

…没有理由不写成:

(setf place expr)

setf的返回值必须是新值.在某些情况下,它可能会导致糟糕的风格,但在这里会很好.

总结

以上是内存溢出为你收集整理的如何防止lisp宏中的表单评估?全部内容,希望文章能够帮你解决如何防止lisp宏中的表单评估?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1039291.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-24
下一篇2022-05-24

发表评论

登录后才能评论

评论列表(0条)

    保存