Как я могу иметь необязательные аргументы И аргументы ключевого слова для одной и той же функции?

#parameters #lisp #keyword

#параметры #lisp #ключевое слово

Вопрос:

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

 (defun max-min (v amp;optional max min amp;keyword (start 0) (end nil))
  

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

 (max-min #(1 2 3 4) :start 1 :end 2)
  

Я получаю сообщение об ошибке Error: :START' is not of the expected type REAL'

Я предполагаю, что это потому, что он пытается привязаться :start к max . Как я могу заставить это работать? Спасибо.

Ответ №1:

Вам нужно вызвать эту функцию с требуемым параметром, необязательными параметрами, а затем параметрами ключевого слова. Как это должно работать иначе? В вашем вызове отсутствуют необязательные параметры. Если вы хотите указать параметры ключевого слова в вызове, необязательные параметры больше не являются необязательными.

 (max-min #(1 2 3 4) 0 100 :start 1 :end 2)
  

Основное правило стиля:

Не смешивайте необязательные параметры с параметрами ключевого слова в функции. Common Lisp, например, использует его в каком-то месте, и это источник ошибок.

CL:READ-FROM-STRING вот такой пример.

 read-from-string string
                 amp;optional eof-error-p eof-value
                 amp;key start end preserve-whitespace
  

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

Это работает:

 (read-from-string " 1 3 5" t nil :start 2)
  

Это также может сработать:

 (read-from-string " 1 3 5" :start 2)
  

Но пользователь забыл указать EOF-ERROR-P и EOF-VALUE. Компилятор Lisp может не жаловаться, и пользователь задастся вопросом, почему он не запускается с 2.

Ответ №2:

Для полноты картины вы можете технически заставить ее работать, проанализировав список предоставленных аргументов самостоятельно:

 (defun max-min (v amp;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 (amp;key (start 0) (end nil)) args
        ;; ...
        ))))
  

Однако, как обычно, неплохо послушать Райнера. Это выглядит как слишком волшебный дизайн. Это сбивает с толку как среду разработки (например, нарушая автоматическое отображение списка аргументов), так и пользователя (затрудняя поиск ошибок типа: что происходит, когда вы случайно передаете ключевое слово там, где вы намеревались передать число?).