#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
;; ...
))))
Однако, как обычно, неплохо послушать Райнера. Это выглядит как слишком волшебный дизайн. Это сбивает с толку как среду разработки (например, нарушая автоматическое отображение списка аргументов), так и пользователя (затрудняя поиск ошибок типа: что происходит, когда вы случайно передаете ключевое слово там, где вы намеревались передать число?).