乱序指定可选参数

Specifying optional parameters out of order

下面的代码实现了一个带有隐式可选参数的宏调用——隐式是因为它隐藏在 &rest 参数中。有没有更好的方法来编写宏(及其支持函数)——也许是通过使用 &optional 关键字(或者其他方式)使可选参数显式?最好是第一个参数是必需的,第二个参数是可选的,其余的许多参数是必需的。如果可能的话,也希望保持宏定义简单,由支持函数完成工作(但也希望学习更高级的方法):

(defstruct action
  name duration slot1 slot2 slot3)

(defmacro define-action (name &rest rest)
  `(install-action ',name ',rest))

(defun install-action (name &rest rest)
  (let* ((slots (first rest))
         (duration (if (numberp (first slots))
                       (prog1 (first slots)
                              (setf slots (cdr slots)))                              
                     0))
         (action (make-action :name name :duration duration
                   :slot1 (first slots) :slot2 (second slots)
                   :slot3 (third slots))))
    (print action)))

(define-action move a b c)  ;first example no duration

(define-action move 1 a b c)  ;second example with duration=1

#S(ACTION :NAME MOVE :DURATION 0 :SLOT1 A :SLOT2 B :SLOT3 C) 
#S(ACTION :NAME MOVE :DURATION 1 :SLOT1 A :SLOT2 B :SLOT3 C)

补充说明:上面的插槽值实际上是表示为(有时是深度嵌套的)lisp 树的各种规范。函数 install-action 解释规范并将其语义内容安装到数据库中。

参数和自变量列表:样式

有明确的参数列表很有用。 Common Lisp 为其提供了广泛的支持。但即便如此,也不是每个参数列表的想法都能得到支持。正如 jkiiski 在他的评论中指出的那样,有一个会说话的参数列表总是有帮助的:它可以帮助开发人员,编译器可以在编译时捕获一些错误,Lisp 可以提供更好的调试信息。

样式规则之一:可选参数应位于参数列表的末尾。 Common Lisp 本身至少有一个地方违反了这一点(现在只能记住一个功能)并且它总是痛苦且容易出错。

修正INSTALL-ACTION的arglist:

(defun install-action (name slots &optional (duration 0))
   ...)

也修复宏的arglist

像这样使用它:

(define-action move (a b c) 1)

最好在宏界面中对事物进行分组。

(defmacro define-action (name slots &optional duration)
   ...)

或者甚至使用名为 arguments 的关键字:

(define-action move :slots (a b c) :duration 1)

变长了,但是可读性大大提高了。

附带问题:我们需要宏 DEFINE-ACTION 吗?为什么?

出现这种宏的主要原因通常是:

  • 少引用
  • 特殊语法
  • 编译时副作用
  • 扩展到其他宏调用