我目前有一个定义如下的宏: 我现在想向此宏添加一个额外的参数,这是用户可以选择提供的一种标志,它将改变宏的行为。下面的代码应该是可能的: 我特意做了一个概念性的例子,重点放在重要的内容上,而不是我要实现的实际宏上。真正的宏是不同的,而且更复杂。如您所见,现在可以使用:flag参数(只要前缀为':',flag可以是任何单词)并且没有flag参数都可以调用该宏。有没有一种方法可以不用将 答案 0 :(得分:3) 和电话是 它可以是 您需要定义宏以采用单个 答案 1 :(得分:2) 人们只能猜测出什么有用,因为宏的设计在某种程度上取决于更多的上下文:实际用于什么。 例如,我们在CLOS中编写 首先出现名称,然后是零个或多个方法限定符(这里是 为此,我们需要自己编写arglist的宏结构,因为它与内置宏arglist模式不匹配。 在其他宏中,我们可能会写 例如类似于 尽管上面使用的是关键字,而不是可选的。 或者我们可能要写: 如果只有三个标志,则还可以生成三个具有不同名称的不同宏并将其删除。 答案 2 :(得分:0) 废话少说:将带有标志的宏语法重写为另一个带有包含标志的固定参数的宏: 一些测试: 答案 3 :(得分:0) 在设计诸如宏之类的东西时要考虑的一件事(记住,当您在设计宏时,您正在设计一种编程语言)是人们期望阅读该语言的方式。如果您正在设计的编程语言是CL的适度超集,或者与CL非常接近,那么您可能不希望违反CL程序员在读取CL代码时的期望,或更普遍的是Lisp程序员在读取Lisp代码时的期望。 [请注意,以下大部分内容是观点:人们显然有不同的观点-这些是我的,他们比任何人都没有权利。] 那么,这些期望是什么?好吧,它们可能包括以下两个: 一个违反这些期望的例子是一个对象系统,它使用某些 好吧,让我们看一下您的宏的两种情况: 这很好。 但这在视觉上处于第二位置,这与宏所涉及的内容无关:它只是一些可选参数。有趣的是,现在是第三名。 那么,我们该如何解决呢?事实证明,CL中已经存在一个很好的示例: 和 这两个都满足最重要的前两个位置的要求,同时允许使用可选参数并在视觉上清楚地指示使用它们的时间。 (除了: 我想都可以。) 因此,重做宏的一种方法就像 或 您可以轻松实现该目标: 事实上,我会做得比这更进一步:人们期望关键字参数具有值,因为在大多数其他地方,它们都具有值。所以有 符合那个期望。这样做还有一个好处,就是您可以使用CL的参数解析来获取信息: 例如。如果您这样编写宏,则可能会得到如下所示的结果:(defmacro some-macro (generic-name (&rest args) &body body)
...)
(some-macro some-name (arg1 arg2) (print (+ arg1 arg2)))
(some-macro :flag some-name (arg1 arg2) (print (special-+ arg1 arg2)))
&optional
关键字放在参数列表的末尾(即,它确实需要在该第一个位置)。 4 个答案:
&optional
只能在位置参数的末尾(可以在&rest
或&body
之后)。即使您可以更早提出来,如果还有&rest
或&body
,它怎么知道您是否提供了可选参数?例如。如果lambda列表是(&optional arg1 arg2 &rest rest-args)
(func-name 1 2 3 4 5)
arg1 = 1, arg2 = 2, rest-args = (3 4 5)
或arg1 = NIL, arg2 = 1, rest-args = (2 3 4 5)
。&rest
参数。然后,您可以检查第一个参数是否为关键字,更新参数列表以添加默认值,然后使用destructuring-bind
进行解析。(defmacro some-macro (&rest all-args)
(unless (keywordp (first all-args))
(push nil all-args)) ;; flag defaults to NIL
(destructuring-bind (flag generic-name (&rest args) &body body) all-args
...))
(some-macro :flag some-name (arg1 arg2) (print (special-+ arg1 arg2)))
(some-macro some-name (arg1 arg2) (print (+ arg1 arg2)))
(defmethod foo :around ((a class-a)) ...)
:around
,然后是arglist。在典型的定义宏(以{{1开头的那些宏中)先在名称前加上一个标志会很奇怪。 }}。def
(defmacro some-macro (name &rest args)
(let* ((qualifiers (loop for arg in args
until (listp arg)
collect (pop args)))
(arg-list (pop args))
(body args))
... ; return something for the example
))
(some-macro some-name (arg1 arg2 :flag)
(print (special-+ arg1 arg2)))
(defmacro some-macro (some-name (arg1 arg2 &optional flag) &body body)
...)
(with-input-from-string (stream string :start 10)
(... ))
(defmacro some-macro-w-flags (flags name (&rest args) &body body)
...)
(defmacro some-macro (&rest args)
(let ((flags))
(loop while (keywordp (car args))
do (push (pop args) flags))
`(some-macro-w-flags ,flags ,@args)))
[1]> (macroexpand-1 '(some-macro abc (1 2 3)))
(SOME-MACRO-W-FLAGS NIL ABC (1 2 3)) ;
T
[2]> (macroexpand-1 '(some-macro :foo abc (1 2 3)))
(SOME-MACRO-W-FLAGS (:FOO) ABC (1 2 3)) ;
T
[3]> (macroexpand-1 '(some-macro :foo :bar abc (1 2 3)))
(SOME-MACRO-W-FLAGS (:BAR :FOO) ABC (1 2 3)) ;
T
(<operator> <thing> ...)
–到目前为止,表单中的前两个子表单最为有趣,而第二个子表单通常比第一个更有趣。考虑一下(defun foo ...)
,(dolist (x ...) ...)
,(let ((x y) ...) ...)
。send
操作显式地与消息传递配合工作(我认为Old Flavors做到了,而New Flavors却没有,但是我的记忆现在很模糊)。使用这些代码编写的代码看起来像(send <object> <message> ...)
:许多形式的第一个单词是send
。这意味着这个视觉上重要的代码读取位置已被完全浪费,因为它始终是同一个单词,而重要的位置现在是第二和第三子窗体。嗯,相反,我们可以忽略整个send
并写(message object ...)
或(object message ...)
。 CLOS本质上采用了这些选项中的第一个,其中“消息”是一个泛型函数,当然,泛型函数可以专注于多个参数,这会破坏整个消息传递范式。但是您可以像编写CLOS一样编写CLOS,并且它可以工作,这意味着它与其他很多CL代码的外观相同。(some-macro some-name ...)
(some-macro :flag some-name ...)
defstruct
。 defstruct
有两种基本情况:(defstruct structure-name
...)
(defstruct (structure-name ...)
...)
defclasss
通过将选项放在末尾来做不同的事情,如:(defclass name (...supers...)
(...slot specifications...)
...options...))
defstruct
。在这种情况下,您将拥有(some-macro some-name (...)
...)
(some-macro (some-name :flag) (...)
...)
(defmacro some-macro (thing (&rest args) &body forms)
(multiple-value-bind (the-thing the-options)
(etypecase thing
(symbol (values thing '()))
(cons
(destructuring-bind (proposed-name . proposed-options) thing
(unless (symbolp proposed-name)
(error ...))
(unless (proper-list-p proposed-options)
(error ...))
(values proposed-name proposed-options))))
...))
(some-macro (some-name :flag t) (...)
...)
> (destructuring-bind (&key (flag nil flagp)) '(:flag t)
(values flag flagp))
t
t
(defmacro some-macro (thing (&rest args) &body forms)
(multiple-value-bind (the-thing flag flagp)
(etypecase thing
(symbol (values thing nil nil))
(cons
(destructuring-bind (proposed-name (&key (flag nil flagp))) thing
(unless (symbolp proposed-name)
(error ...))
(values proposed-name flag flagp))))
...))