LINUX.ORG.RU

Помогите разобраться с мультиметодами.

 , , , ,


1

3

Я пытаюсь найти пример, по которому можно четко понять преимущество мультиметодов над одиночной диспетчеризацией и message-passing. Оговорюсь, на всякий случай, что это преимущество я не пытаюсь отрицать, просто до меня пока не доходит.

Сначала пытался понять пример с астероидами и кораблями с википедии.

https://ru.wikipedia.org/wiki/Мультиметод#Common_Lisp

Но,по этому примеру невозможно ничего понять, потому-что, на мой взгляд, задача сформулированна неверно. Во-первых, столкновение это событие. Причем здесь функция collide — абсолютно непонятно. Функция может быть инициатором события, а нам в данной задаче нужна реакция на событие. Во-вторых, когда сталкиваются 2 объекта, вопрос о том кто с кем столкнулся не имеет никакого смысла, тут невозможно выделить пассивную и активную сторону, активной стороной являются обстоятельства, которые привели к столкновению. Поэтому, пример, мягко говоря синтетический, и ничего не проясняет. Ну, и кроме того, в том виде он тривиально пишется и в «традиционном» стиле.

Затем я пытался понять преимущество мультиметодов на этом вот примере из PCL

http://spline-online.tk/wiki/doku.php?id=pcl:chapter16#defmethod

С банковским «приложением», но снова ничего не понял. Все что там показывается, без мультиметодов, по моему, будет не хуже, и не сложней.

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

Подскажите пожалуйста, где можно такое найти, или, если не трудно, приведите пример, демонстрирующий преимущество.



Последнее исправление: asteroidcollide (всего исправлений: 2)
Ответ на: комментарий от monk

нет, этот вариант диспетчеризации определен только для Baz и его наследников. Так оно и должно быть. Наследники SpaceObject ничего не знают, и не должны знать об этом варианте. Чтобы было такое поведение нужно его унаследовать от Baz

asteroidcollide
() автор топика
Ответ на: комментарий от asteroidcollide

Чтобы было такое поведение нужно его унаследовать от Baz

Вот и я про это же. Более того, от имени это поведение вообще никак не зависит.

Quux := Baz clone setName("nomoreplanet")

quux:= Quux clone

foo collide(bar, quux)

#>>>> asteroid collide with spaceship
#>>>> just another behavor for asteroid with planet
monk ★★★★★
()
Ответ на: комментарий от asteroidcollide

Чтобы было такое поведение нужно его унаследовать от Baz

Кстати в лиспе можно и реально по полю name


(defun select (x)
  (cond
    ((string= (name x) "planet") 'planet)
    ((string= (name x) "asteroid") 'asteroid)))

(defun collide (a &rest args)
  (dolist (x args) (multi (select a) (select x) a x)))

(defmethod multi (name-a name-b a b)
  (format t "~a collide with ~a~%" (name a) (name b)))

(defmethod multi (name-a (name-b (eql 'planet)) a b)
  (format t "just another behavor for ~a with ~a~%" (name a) (name b)))

(defclass foo () ((name :initform "asteroid" :reader name)))
(defclass bar () ((name :initform "spaceship" :reader name)))
(defclass baz () ((name :initform "planet" :reader name)))
(defclass quux () ((name :initform "planet" :reader name)))

(let ((foo (make-instance 'foo))
      (bar (make-instance 'bar))
      (baz (make-instance 'baz)))
  (collide foo bar baz)
  (collide foo bar quux) ;; выводит то же, что и (collide foo bar baz)
  (collide bar foo baz)
  (collide baz foo bar))

monk ★★★★★
()
Ответ на: комментарий от monk

Вот и я про это же

нет, Вы про другое:)

Более того, от имени это поведение вообще никак не зависит.

Зависит, протсто там метод был так определен, без обращения к имени

SpaceObject := Object clone do(
     name ::= nil
     collide := method(
       call evalArgs foreach(visitor, visitor  multyMethod(self))
     )
     multyMethod ::= method(spaceObject, writeln(spaceObject name ..  " collide with " .. self name)) // default 
)


Foo := SpaceObject clone setName("asteroid")
Bar := SpaceObject clone setName("spaceship")
Baz := SpaceObject clone setName("planet") setMultyMethod(
  method(spaceObject, writeln("just another behavor for " .. spaceObject name .. " with " .. self name))
)
Quux := Baz clone

foo := Foo clone
bar := Bar clone
baz := Baz clone
quux := Baz clone setName("blabla")



foo collide(bar, quux)
#>>>>asteroid collide with spaceship
#>>>>just another behavor for asteroid with blabla
#>>>>

asteroidcollide
() автор топика
Ответ на: комментарий от asteroidcollide

Это не в счет. Меня интересует только вариант на чистых мультиметодах

Можно всюду заменить defun на defmethod. Будет также работать.

(defmethod select (x)
  (cond
    ((string= (name x) "planet") 'planet)
    ((string= (name x) "asteroid") 'asteroid)))

(defmethod collide (a &rest args)
  (dolist (x args) (multi (select a) (select x) a x)))
...

monk ★★★★★
()
Ответ на: комментарий от monk

(cond

Ну, вот, кстати, мы и вернулись к вопросу о том, что одна из ключевых фич — замена веток в рантайме:) Тут уже мультиметоды теряют почти всякий смысл

gototheultimate
()
Ответ на: комментарий от gototheultimate

Ну, вот, кстати, мы и вернулись к вопросу о том, что одна из ключевых фич — замена веток в рантайме

Так селектор никто не мешает в рантайме менять. Можно даже расширять.

;; deathstar module
(let ((super (fdefinition 'select)))
  (setf (fdefinition 'select)
    (lambda (x)
      (if (string= (name x) "deathstar")
          'deathstar
          (funcall super x)))))

monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.