如何定义取代Lisp中& optional参数的& key参数

时间:2014-04-21 16:43:35

标签: common-lisp

我刚刚开始写这个函数,我想知道是否有办法,如果只输入& key参数,可以忽略&可选列表。

(defun test (&optional arg (i 0) &key (size s))
  ...)

我希望能够运行

(test arg)

(test arg i)

但也

(test :size)

现在这是一个更好的模拟,但我不知道放在哪里:大小在参数列表中

    (defun test (&optional arg (i 0))
      (cond ((eq arg nil) (return-from test (test-1)))
        ((listp arg)
         (return-from test (test-2 arg)))
        ((pointerp arg) (mem-aref (test-3 arg) :int i))
            (:size (size-test arg))
        (t nil)))

    so i can run (test) and get:

    <output of (test-1)>


    I can run (test '(1 2 3)) and get:


    <output of (test-2 arg)>


    I can run (test <pointer> 0)

    and output is:

    <output of (mem-aref (test-3 arg) :int i)>

    I can run (test :size) and get:

    <output of (test-size arg)>

4 个答案:

答案 0 :(得分:6)

您必须使用&rest参数列表并在函数中处理它。

应避免混合使用可选和关键字参数。使用可选和关键字参数是BAD风格。这是使用少数函数的无数错误的来源(如READ-FROM-STRING)。

答案 1 :(得分:6)

混合可选和关键字参数

混合可选和关键字参数仍然不是那么容易做到的事情。如果函数接受可选参数,那么除非还提供了可选参数,否则您将无法使用关键字参数。否则,第一个关键字将被解释为可选参数,依此类推。例如,请参阅此Stack Overflow问题:How can I have optional arguments AND keyword arguments to the same function?。正如该问题的答案所指出的那样,混合可选和关键字参数通常是容易出错的做法。 Common Lisp用read-from-string来做,它经常导致人们陷入困境。

你提出的建议不仅仅是有一个同时使用关键字和可选参数的函数,但是从它的声音来看,它实际上正在检查参数的类型,并采取一种行为。一个案件​​,另一个案件。在这种情况下,如果i应该是一个数字,那么你可以检查第一个参数,如果它是一个数字,则将其视为可选参数,其余作为关键字参数,如果不是一个数字,然后将整个列表视为关键字参数。您可以使用以不同方式进行结构化的&rest参数来执行此操作:

(defun frob (&rest args)
  (flet ((frob-driver (i size)
           (list i size)))
    (if (or (endp args) (numberp (first args)))
        ;; no args, or the first argument is a number (and thus
        ;; not a keyword argument)...
        (destructuring-bind (&optional (i 'default-i) &key (size 'default-size)) args
          (frob-driver i size))
        ;; otherwise, there are some non-numeric arguments at 
        ;; beginning, so it must be the keyword list, and that the
        ;; "optional" wasn't provided.
        (destructuring-bind (&key (size 'default-size) &aux (i 'default-i)) args
          (frob-driver i size)))))
(frob 10 :size 50)             ; give i and size
;=> (10 50)

(frob :size 60)                ; give size, but not i
;=> (default-i 60)

(frob 40)                      ; give i, but not size
;=> (40 default-size)

(frob)                         ; give neither
;=> (default-i default-size)

没有关键字符号的关键字参数

在评论中,您提到您希望能够在参数列表中使用非关键字符号作为关键字。这很容易。在HyperSpec中,§3.4.1 Ordinary Lambda Lists描述了关键字参数的语法:

[&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]] 

这意味着您可以定义如下函数:

(defun frob (&key foo ((bar-keyword bar-variable) 'default-baz))
  (list foo bar-variable))
(frob :foo 1 'bar-keyword 2)
;=> (1 2)

(frob :foo 3)
;=> (3 default-baz)

(frob 'bar-keyword 2)
;=> (nil 2)

答案 2 :(得分:0)

简而言之,&#34; no&#34;。没有办法实现这一点。作为一般规则,请尽量避免混合&rest&optional&key参数,因为它们的互动很微妙,并且会更频繁地绊倒你而不是实际有用。

此外,如果:size是关键字参数,则(test :size)缺少一个参数(绑定size的值)。您最好的选择可能是arg,看看它是:size还是其他。

答案 3 :(得分:0)

发布答案,因为我解决了这里的主要问题是我想出的结果代码,cond语句是什么问题。 &amp; args用法创建了另一个问题,正在讨论该帖子here((symbolp (cadr args)) (%vector-float-size (first args)))这句话就是我从约书亚·泰勒(Joshua Taylors)那里得到的,是一本善意的文章和非常有用的答案。

(defun vector-float (&rest args)
  (cond ((eq (first args) nil) (return-from vector-float (%vector-float)))
    ((listp (first args))
     (c-arr-to-vector-float (first args)))
    ((symbolp (cadr args)) (%vector-float-size (first args)))
    ((pointerp (first args)) (mem-aref (%vector-float-to-c-array (first args)) :float (second args)))
    (t nil)))