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

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

(defun max-min (v &optional max min &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. Как я могу заставить это работать? Спасибо.

Ответы [ 2 ]

8 голосов
/ 13 ноября 2011

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

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

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

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

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

read-from-string string
                 &optional eof-error-p eof-value
                 &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.

5 голосов
/ 13 ноября 2011

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

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

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

...