Вызов следующего наиболее конкретного метода не работает

Рассмотрим класс account :

(defclass account ()
  ((name :initarg :name :reader name)
   (balance :initarg :balance :initform 0.00 :accessor balance)
   (interest-rate :allocation :class :initform 0.06
                  :reader interest-rate)))

Для этого класса мы определяем метод withdraw :

(defmethod withdraw ((acct account) amt)
  (if (< amt (balance acct))
      (decf (balance acct) amt)
      'insufficient-funds))

И еще один класс password-account , который является подклассом account:

(defclass password-account (account)
  ((password :initarg :password :reader password )))

И метод withdraw для этого класса:

(defmethod withdraw ((acct password-account) amt pass)
  (if (equal (password acct) pass)
      (call-next-method acct amt )
      'wrong-password))

Но это дает ошибку:

The generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::WITHDRAW (1)>
takes 2 required arguments; was asked to find a method with
specializers
(#<STANDARD-CLASS COMMON-LISP-USER::PASSWORD-ACCOUNT>
 #1=#<SB-PCL:SYSTEM-CLASS COMMON-LISP:T> #1#)
   [Condition of type SB-PCL::FIND-METHOD-LENGTH-MISMATCH]
See also:
  Common Lisp Hyperspec, FIND-METHOD [:function]

Restarts:
 0: [RETRY] Retry SLIME REPL evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1005308033}>)

Почему это происходит? И что делает

попросили найти метод со специалистами

значит здесь?

Здесь у основной функции withdraw было два аргумента acct и amt, поэтому, чтобы вызвать ее из более конкретного метода, который использует 3 аргумента вместо 2, мы можем предоставить call-next-method аргументы менее конкретного метода withdraw. Но это все еще не работает.
Любая помощь приветствуется.


person Mooncrater    schedule 18.06.2017    source источник
comment
У вас не может быть метода с двумя обязательными параметрами и другого с тремя обязательными аргументами в одной и той же универсальной функции. CLOS не позволяет этого. Первое определение метода определяет количество необходимых аргументов. Общая функция в вашем примере имеет два обязательных аргумента, и вы пытаетесь определить метод с тремя обязательными аргументами. Это не поддерживается, а также не разрешено.   -  person Rainer Joswig    schedule 18.06.2017
comment
Спасибо @RainerJoswig. Есть ли какая-то конкретная причина не поддерживать такие методы?   -  person Mooncrater    schedule 18.06.2017
comment
И какой смысл вызывать следующий наиболее конкретный метод через call-next-method с аргументами? Поскольку все методы потребуют одинакового количества аргументов. (При этом те же аргументы)   -  person Mooncrater    schedule 18.06.2017
comment
Универсальная функция должна иметь одно действительное количество обязательных аргументов. Если бы это было не так, компилятор не смог бы проверить, что было предоставлено правильное количество обязательных аргументов.   -  person Rainer Joswig    schedule 18.06.2017
comment
Нет, не все методы требуют одинакового количества аргументов. Все, что требуется, это чтобы у методов было одинаковое количество НЕОБХОДИМЫХ аргументов. Методы могут иметь дополнительные аргументы: необязательные аргументы, аргументы ключевого слова, остальные аргументы.   -  person Rainer Joswig    schedule 18.06.2017
comment
@RainerJoswig Итак, сделать аргумент pass необязательным должно помочь.   -  person Mooncrater    schedule 18.06.2017


Ответы (1)


Конгруэнтные лямбда-списки для универсальных функций

Методы универсальной функции должны быть конгруэнтны лямбда-списки. Стандарт языка описывает, что это означает: Функция.

Как видите, первое правило гласит:

  • Каждый лямбда-список должен иметь одинаковое количество обязательных параметров.

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

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

Необязательные параметры также должны совпадать

Также обратите внимание, что все методы универсальной функции должны иметь одинаковое количество необязательных параметров. См. второе правило в стандарте.

Формулировка

  • параметр — это что-то вроде именованной переменной в лямбда-списке.
  • аргумент предоставляется при вызове функции

Примеры:

(defun foo (a b) (list a b))

a и b — это параметры для функции foo.

(foo (+ 2 3) (* 4 5))

5 и 20 — это два аргумента для вызова функции foo.

person Rainer Joswig    schedule 18.06.2017