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

use*_*736 3 common-lisp

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

(defun test (&optional arg (i 0) &key (size s))
  ...)
Run Code Online (Sandbox Code Playgroud)

我希望能够跑

(test arg)
Run Code Online (Sandbox Code Playgroud)

要么

(test arg i)
Run Code Online (Sandbox Code Playgroud)

但是也

(test :size)
Run Code Online (Sandbox Code Playgroud)

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

    (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)>
Run Code Online (Sandbox Code Playgroud)

Rai*_*wig 6

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

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


Jos*_*lor 6

混合可选和关键字参数

混合可选和关键字参数仍然不是那么容易做到的事情.如果函数接受可选参数,那么除非还提供了可选参数,否则您将无法使用关键字参数.否则,第一个关键字将被解释为可选参数,依此类推.例如,请参阅此Stack Overflow问题:如何为同一函数提供可选参数AND关键字参数?.正如该问题的答案所指出的那样,混合可选和关键字参数通常是容易出错的做法.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)))))
Run Code Online (Sandbox Code Playgroud)
(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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

(frob 'bar-keyword 2)
;=> (nil 2)
Run Code Online (Sandbox Code Playgroud)


use*_*736 0

发布为答案,因为我解决了这里的主要问题是我想出的结果代码, cond 语句才是最重要的。&args 的使用产生了另一个问题,该帖子正在此处讨论。这((symbolp (cadr args)) (%vector-float-size (first args)))句话是我从约书亚·泰勒(Joshua Taylor)亲切的书面回答中得出的,内容非常丰富。

(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)))
Run Code Online (Sandbox Code Playgroud)