Как потребовать аргументы ключевого слова в Common Lisp?

Данный

(defun show-arg (a)
  (format t "a is ~a~%" a))

(defun show-key (&key a)
  (format t "a is ~a~%" a))

оценка

(show-arg)

приведет к ошибке "недопустимое количество аргументов: 0", где

(show-key)

отобразит a is NIL

Как я могу заставить SHOW-KEY сигнализировать об ошибке, как SHOW-ARG? Есть ли другой способ, кроме использования (unless a (error "a is required")) в теле функции? Мне очень нравятся аргументы ключевых слов, я использую их постоянно и почти всегда хочу, чтобы они были обязательными.


person Dan Passaro    schedule 25.05.2016    source источник


Ответы (2)


Аргументы ключевого слова всегда необязательны, поэтому вам нужно вручную проверить, заданы ли они, и при необходимости сигнализировать об ошибке. Однако было бы лучше не требовать аргументы ключевого слова. Компилятор не распознает их как требуемый и, следовательно, не выдаст вам сообщение об ошибке при отсутствии аргументов во время компиляции.

Если вы действительно хотите, чтобы они требовались, вы можете указать аргументы с помощью трехэлементного списка; первый элемент является аргументом, второй - значением по умолчанию, а третий - переменной, которая будет истинной, если аргумент был указан. Проверка третьего элемента лучше, чем проверка самого ключевого слова, потому что тогда вы можете определить разницу между NIL, которое было по умолчанию, и NIL, которое пользователь указал в качестве аргумента.

(defun foo (&key (keyarg nil keyargp))
  (unless keyargp (error "KEYARG is required."))
  (* keyarg 2))

Изменить

Теперь, когда я думаю об этом немного больше, на самом деле есть способ получить ошибки времени компиляции из-за отсутствия аргументов ключевого слова. Определите макрос компилятора для функции:

(defun foo (&key a b c d)
  (* a b c d))

(define-compiler-macro foo (&whole whole &key (a nil ap) (b nil bp)
                                   (c nil cp) (d nil dp))
  (declare (ignore a b c d))
  (unless (and ap bp cp dp)
    (error "Missing arguments..."))
  whole)
person jkiiski    schedule 25.05.2016
comment
Однако было бы лучше не требовать аргументы ключевого слова. Каким будет предложенный способ, например, инициализировать запись с множеством обязательных полей? Например. как я могу улучшить читаемость чего-то вроде (make-album "Led Zeppelin" "Physical Graffiti" 1979 nil t 'rock)? - person Dan Passaro; 26.05.2016
comment
@ leo-the-manic Обычно вы пытаетесь предоставить значения по умолчанию, которые имеют смысл, и пользователь будет переопределять их только при необходимости. В этом примере первые два могут быть единственными обязательными аргументами, а остальные можно оставить как NIL. В большинстве реальных программ это не проблема для удобочитаемости, потому что редко инициализируют записи буквальными значениями. Обычно вы читаете значения из файла / базы данных / пользователя, и имена переменных дадут читателю понять, что они означают. Они также могут использовать свою среду разработки, например SLIME, для просмотра списка аргументов. - person jkiiski; 26.05.2016

Один из возможных вариантов:

(defun foo (&key (arg1 (error "missing arg1 in call to function foo")))
   arg1)

Используй это:

CL-USER 80 > (foo)

Error: missing arg1 in call to function foo
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

Это приведет к ошибке во время выполнения, к сожалению, не во время компиляции.

person Rainer Joswig    schedule 25.05.2016
comment
Небольшой вариант заключается в том, чтобы обернуть сигнализацию об ошибке в небольшую функцию, чтобы вы могли сказать &key (foo (required)). - person Svante; 25.05.2016