乱序指定可选参数
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 吗?为什么?
出现这种宏的主要原因通常是:
- 少引用
- 特殊语法
- 编译时副作用
- 扩展到其他宏调用
下面的代码实现了一个带有隐式可选参数的宏调用——隐式是因为它隐藏在 &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 吗?为什么?
出现这种宏的主要原因通常是:
- 少引用
- 特殊语法
- 编译时副作用
- 扩展到其他宏调用