如何为同一个函数提供可选参数AND关键字参数?

Dan*_*iel 5 lisp parameters keyword

我正在尝试编写一个可以使用可选和关键字参数的Lisp函数.功能开始了

(defun max-min (v &optional max min &keyword (start 0) (end nil))
Run Code Online (Sandbox Code Playgroud)

当我尝试使用关键字参数而不是可选的参数调用函数时,我收到错误.我想做的是

(max-min #(1 2 3 4) :start 1 :end 2)
Run Code Online (Sandbox Code Playgroud)

我收到了错误 Error: :START' is not of the expected type REAL'

我认为这是因为它试图绑定:startmax.我怎样才能让它发挥作用?谢谢.

Rai*_*wig 8

您需要使用必需参数,可选参数和关键字参数调用该函数.它应该如何运作呢?您的通话缺少可选参数.如果要在调用中指定关键字参数,则可选不再是可选的.

(max-min #(1 2 3 4) 0 100 :start 1 :end 2)
Run Code Online (Sandbox Code Playgroud)

基本风格规则:

不要在函数中将可选项与关键字参数混合使用.例如,Common Lisp在某些地方使用它,它是bug的来源.

CL:READ-FROM-STRING 就是这样一个例子.

read-from-string string
                 &optional eof-error-p eof-value
                 &key start end preserve-whitespace
Run Code Online (Sandbox Code Playgroud)

http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_fro.htm

这有效:

(read-from-string " 1 3 5" t nil :start 2)
Run Code Online (Sandbox Code Playgroud)

这可能也有效:

(read-from-string " 1 3 5" :start 2)
Run Code Online (Sandbox Code Playgroud)

但是用户忘了指定EOF-ERROR-P和EOF-VALUE.Lisp编译器可能不会抱怨,用户会想知道为什么它不会从2开始.


Mat*_*ard 5

为了完整起见,您可以通过自己解析提供的参数列表在技术上使其工作:

(defun max-min (v &rest args)
  (flet ((consume-arg-unless-keyword (default)
           (if (keywordp (first args))
               default
               (pop args))))
    (let ((max (consume-arg-unless-keyword nil))
          (min (consume-arg-unless-keyword nil)))
      (destructuring-bind (&key (start 0) (end nil)) args
        ;; ...
        ))))
Run Code Online (Sandbox Code Playgroud)

像往常一样,听Rainer是个好主意.这看起来太神奇了.它会混淆开发环境(例如,通过打破自动arglist显示)和用户(通过使类型错误更难找到:当你意外传递一个你打算传递数字的关键字时会发生什么?).