Я только начал писать эту функцию, и мне было интересно, есть ли способ, чтобы, если был введен только аргумент & key, необязательный список & можно было бы игнорировать.

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

Я хотел бы иметь возможность бегать

(test arg)

Или

(test arg i)

Но и

(test :size)

Теперь это лучший макет, но я не знаю, куда поместить: 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
user3517736 21 Апр 2014 в 20:43

4 ответа

Лучший ответ

Публикация в качестве ответа, потому что я решил, что основная проблема здесь - это результирующий код, который я придумал. Важны операторы cond. Использование & args создало еще одну проблему, и этот пост обсуждается здесь. Строка ((symbolp (cadr args)) (%vector-float-size (first args))) - это то, что я получил от Джошуа Тейлорса, любезно написанного и чрезвычайно информативного ответа.

(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)))
0
Community 23 Май 2017 в 11:49

Смешивание необязательных и ключевых аргументов

Совмещение необязательных аргументов и аргументов ключевого слова по-прежнему не так просто. Если функция принимает необязательный аргумент, вы не сможете использовать аргументы ключевого слова, если не указан необязательный аргумент. В противном случае первое ключевое слово будет интерпретироваться как необязательный аргумент и так далее. См., Например, этот вопрос о переполнении стека: Как я могу использовать дополнительные аргументы И аргументы ключевого слова для одной и той же функции?. Как следует из ответа на этот вопрос, смешивание необязательных аргументов и аргументов ключевого слова, как правило, подвержено ошибкам. 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)

Аргументы ключевого слова без символов ключевого слова

В комментариях вы упомянули, что хотели бы иметь возможность использовать символы без ключевых слов в качестве ключевых слов в списках аргументов. Это достаточно просто. §3.4.1 Обычные списки лямбда в HyperSpec описывает синтаксис для аргументов ключевого слова:

[&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)
7
Community 23 Май 2017 в 11:57

Вы должны использовать список аргументов &rest и обработать его в функции.

Следует избегать смешивания необязательных аргументов и аргументов ключевого слова. Использование необязательных аргументов и аргументов с ключевыми словами - ПЛОХО. Это было источником бесчисленных ошибок с несколькими функциями, которые его используют (например, READ-FROM-STRING).

6
Rainer Joswig 21 Апр 2014 в 17:53

Короче «нет». Этого невозможно добиться. Как правило, старайтесь не смешивать аргументы &rest, &optional и &key, поскольку их взаимодействие неуловимо и будет чаще сбивать вас с толку, чем быть полезным.

Кроме того, если :size является аргументом ключевого слова, тогда в (test :size) отсутствует один аргумент (значение, к которому нужно привязать size). Лучше всего, вероятно, посмотреть arg, чтобы узнать, :size это или что-то еще.

0
Vatine 21 Апр 2014 в 17:02