首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >与-主义-绑定

与-主义-绑定
EN

Code Review用户
提问于 2012-04-13 03:19:32
回答 2查看 244关注 0票数 2

4(根据Rainer Joswig的反馈进行修改):

代码语言:javascript
运行
复制
(defmacro with-gensyms ((&rest names) &body body)
  `(let ,(loop for n in names collect `(,n (gensym)))
     ,@body))

(defmacro let-alist ((&rest lookups) alist &body body)
  (with-gensyms (alist-form)
    `(let ((,alist-form ,alist)) 
       (let ,(loop for l in lookups 
           if (keywordp l)
             collect `(,(intern (symbol-name l)) (cdr (assoc ,l ,alist-form)))
           else
             collect `(,l (cdr (assoc ',l ,alist-form))))
     ,@body))))

这个修订版仍然可以用同样的方式使用。

代码语言:javascript
运行
复制
> (defparameter test '((|foo| . 1) (:bar . 2) (baz . 3)))
TEST
> (let-alist (:bar |foo| baz) test
       (list |foo| bar baz))
(1 2 3)

但是宏只展开传入的alist表单一次。以下是slime-macroexpand-1的输出:

代码语言:javascript
运行
复制
(LET ((#:G1299 TEST))
  (LET ((BAR (CDR (ASSOC :BAR #:G1299)))
        (|foo| (CDR (ASSOC '|foo| #:G1299)))
        (BAZ (CDR (ASSOC 'BAZ #:G1299))))
    (LIST |foo| BAR BAZ)))

欢迎所有评论,但具体而言(按重要性下降的顺序排列),

  • 你能想到一个好的文档吗?
  • 你能想到这会爆炸的情况吗?
  • 是否有一个内置宏做同样的事情,或接近它?
EN

回答 2

Code Review用户

回答已采纳

发布于 2012-04-14 13:53:07

它的主要问题是alist- -宏的变量。

代码语言:javascript
运行
复制
CL-USER > (pprint (macroexpand-1 '(let-alist (:bar |foo| baz)
                                      (this-function-takes-a-long-time-or-has-side-effects)
                                    (list |foo| bar baz))))

(LET ((BAR (CDR (ASSOC :BAR (THIS-FUNCTION-TAKES-A-LONG-TIME-OR-HAS-SIDE-EFFECTS))))
      (|foo|
       (CDR (ASSOC '|foo| (THIS-FUNCTION-TAKES-A-LONG-TIME-OR-HAS-SIDE-EFFECTS))))
      (BAZ
       (CDR (ASSOC 'BAZ (THIS-FUNCTION-TAKES-A-LONG-TIME-OR-HAS-SIDE-EFFECTS)))))
  (LIST |foo| BAR BAZ))

如您所见,函数被多次展开到代码中。

规则1:始终检查扩展是否有问题。

规则2:不要将相同的代码多次展开到代码中。

阅读Paul的“On”(免费下载),了解公共Lisp中宏的各种缺陷,以及如何处理这些缺陷。

对于上面的内容,您需要生成一个新的唯一变量并绑定一次值。

票数 2
EN

Code Review用户

发布于 2012-04-14 18:02:54

这里有一个实现,它允许对作者中的值进行读和写访问。

首先,定义一个使用符号-宏的宏,其中主体中的绑定符号展开为在运算符中访问该符号的值的代码:

代码语言:javascript
运行
复制
(defmacro with-alist% (alist-entries instance-form &body body)
  `(symbol-macrolet 
     ,(loop for (alist-binding alist-entry) in alist-entries
            collect `(,alist-binding (cdr (assoc ',alist-entry ,instance-form))))
     ,@body))

并测试它:

代码语言:javascript
运行
复制
(defparameter *al* (list (cons 'foo 5) (cons 'bar 'a) (cons 'baz "z")))
*AL*
CL-USER> 
(print *al*)

((FOO . 5) (BAR . A) (BAZ . "z")) 
((FOO . 5) (BAR . A) (BAZ . "z"))
CL-USER> 
(with-alist% ((foo foo) (bar bar)) *al*
  (format t "foo=~a, bar=~a~%" foo bar)
  (setf foo 1)
  (setf bar "a") ...)
foo=5, bar=A
foo=1, bar=a 
NIL
CL-USER> 
(print *al*)

((FOO . 1) (BAR . "a") (BAZ . "z")) 
((FOO . 1) (BAR . "a") (BAZ . "z"))
CL-USER> 

请注意,不仅可以使用绑定符号进行打印,还可以使用setf符号,这将映射到更改与该符号相关的值。这是使用符号-宏(而不是let)提供的。另外,如果您阅读了具有-槽的源代码,至少对于CCL,符号-宏是如何实现该宏的。再一次,我正致力于一种类似的-插槽句法感觉。

如果您有一个混合关键字和符号的列表,with- but %的效果就足够好了,但是我认为大多数情况下只有符号,这意味着在使用该函数时会有大量的代码重复,例如:

代码语言:javascript
运行
复制
(with-alist% ((foo foo) (bar bar)) *al*

请注意如何列出每个列表条目两次。如果你希望绑定符号和列表条目总是一样的话,该怎么办?如果你能这样做就太好了:

代码语言:javascript
运行
复制
(with-alist (foo bar) *al*

而这个宏:

代码语言:javascript
运行
复制
(defmacro with-alist (alist-entries instance-form &body body)
  `(with-alist% ,(mapcar (lambda (alist-entry)
                           (if (consp alist-entry)
                             `,alist-entry
                             `,(list alist-entry alist-entry)))
                         alist-entries)
                ,instance-form
                ,@body))

用法:

代码语言:javascript
运行
复制
(print *al*)
CL-USER> 
((FOO . 1) (BAR . "a") (BAZ . "z")) 
((FOO . 1) (BAR . "a") (BAZ . "z"))
CL-USER> 
(with-alist (foo (bar% bar)) *al*
  (format t "foo=~a, bar=~a~%" foo bar%)
  (setf foo 5)
  (setf bar% 'a) ...)
foo=1, bar=a
foo=5, bar=A
NIL
CL-USER> 
(print *al*) 

((FOO . 5) (BAR . A) (BAZ . "z")) 
((FOO . 5) (BAR . A) (BAZ . "z"))
CL-USER> 
REPL    

我同意Rainer关于只评估实例-表单一次的评论,当您只想要读取访问权限时。但是,如果您想要进行读/写访问,那么每次在正文中找到绑定符号时,都需要展开实例窗体。否则,您将写入实例-表单的let绑定;而不是实例-表单;因此,实例-表单不会更改。

对于通用Lisp宏编程来说,一个很好的阅读绝对是在Lisp上,而且还可以在Lambda上进行。通过LOL是一项重要的时间投资,但如果你想提高你常用的lisp宏编程技能的话,这是很值得的。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/10843

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档