Множественная отправка - Multiple dispatch

Множественная отправка или мультиметоды это особенность некоторых языки программирования в котором функция или метод возможно динамически отправляется на основе время выполнения (динамический) тип или, в более общем случае, какой-либо другой атрибут более чем одного из его аргументы.[1] Это обобщение разовая отправка полиморфизм где вызов функции или метода динамически отправляется на основе производного типа объекта, для которого был вызван метод. Множественная отправка направляет динамическую отправку к реализующей функции или методу, используя комбинированные характеристики одного или нескольких аргументов.

Понимание отправки

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

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

В более традиционном, т. Е. разовая отправка объектно-ориентированные языки программирования, при вызове метода (отправка сообщения в Болтовня, вызов функции-члена в C ++ ), один из его аргументов обрабатывается специально и используется для определения, какой из (потенциально многих) классов методов с таким именем должен быть применен. На многих языках специальный аргумент указывается синтаксически; например, в ряде языков программирования специальный аргумент ставится перед точкой при вызове метода: special.method (другое, аргументы, здесь), так что lion.sound () издал бы рев, тогда как sparrow.sound () будет издавать чириканье.

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

В Общая объектная система Lisp (CLOS) - ранний и хорошо известный пример множественной отправки.

Типы данных

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

В языках программирования, которые откладывают идентификацию типа данных до времени выполнения (т.е. позднее связывание ), тогда должен произойти выбор среди альтернативных функций на основе динамически определяемых типов аргументов функции. Функции, альтернативные реализации которых выбираются таким образом, обычно называются мультиметоды.

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

Использование на практике

Чтобы оценить, насколько часто на практике используется множественная отправка, Muschevici et al.[2] изучил программы, использующие динамическую диспетчеризацию. Они проанализировали девять приложений, в основном компиляторы, написанные на шести разных языках: Общая объектная система Lisp, Дилан, Сесил, MultiJava, Diesel и Nice. Их результаты показывают, что 13–32% универсальных функций используют динамический тип одного аргумента, а 2,7–6,5% из них используют динамический тип нескольких аргументов. Остальные 65–93% универсальных функций имеют один конкретный метод (переопределитель) и, следовательно, не считаются использующими динамические типы своих аргументов. Кроме того, в исследовании сообщается, что 2–20% общих функций имели две и 3–6% имели три реализации конкретных функций. Числа быстро уменьшаются для функций с более конкретными переопределителями.

Множественная отправка используется гораздо чаще в Юля, где множественная отправка была центральной концепцией дизайна с самого начала языка: собирая ту же статистику, что и Muschevici по среднему количеству методов на универсальную функцию, было обнаружено, что Julia стандартная библиотека использует более чем в два раза большую перегрузку, чем на других языках, проанализированных Muschevici, и более чем в 10 раз в случае бинарные операторы.[3]

Данные из этих документов обобщены в следующей таблице, где коэффициент отправки DR - среднее количество методов на универсальную функцию; соотношение выбора CR является средним квадратом количества методов (чтобы лучше измерить частоту функций с большим количеством методов);[2][3] а степень специализации «DoS» - это среднее количество аргументов, зависящих от типа, на метод (т. е. количество аргументов, которые отправляются):

ЯзыкСреднее количество методов (DR)Коэффициент выбора (CR)Степень специализации (DoS)
Сесил[2]2.3363.301.06
Common Lisp (CMU )[2]2.036.341.17
Общий Лисп (McCLIM )[2]2.3215.431.17
Общий Лисп (Steel Bank )[2]2.3726.571.11
Дизель[2]2.0731.650.71
Дилан (Гвидион)[2]1.7418.272.14
Дилан (OpenDylan)[2]2.5143.841.23
Юля[3]5.8651.441.54
Юлия (только операторы)[3]28.1378.062.01
MultiJava[2]1.508.921.02
Отлично[2]1.363.460.33

Теория

Теория нескольких языков диспетчеризации была впервые разработана Castagna et al., Определив модель для перегруженных функций с помощью позднее связывание.[4][5] Это дало первую формализацию проблема ковариантности и контравариантности объектно-ориентированных языков[6] и решение проблемы бинарных методов.[7]

Примеры

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

Языки со встроенной множественной рассылкой

C #

C # добавлена ​​поддержка динамических мультиметодов в версии 4[8] (Апрель 2010 г.) с использованием ключевого слова dynamic. В следующем примере демонстрируются мультиметоды в сочетании с выражениями переключения, введенными в версии 8. [9] (Сентябрь 2019 г.). Как и многие другие языки со статической типизацией, C # также поддерживает перегрузку статических методов.[10] Microsoft ожидает, что разработчики предпочтут статическую типизацию вместо динамической в ​​большинстве сценариев.[11] Ключевое слово dynamic поддерживает взаимодействие с COM-объектами и языками .NET с динамической типизацией.

   класс Программа   {        статический пустота Главный(нить[] аргументы)        {            // Статическая отправка методу Collider.Collide            Консоль.WriteLine(Коллайдер.Столкнуться(новый Астероид(101), новый Космический корабль(300)));            Консоль.WriteLine(Коллайдер.Столкнуться(новый Астероид(10), новый Космический корабль(10)));            Консоль.WriteLine(Коллайдер.Столкнуться(новый Космический корабль(101), новый Космический корабль(10)));        }    }    статический класс Коллайдер    {        общественный статический нить Столкнуться(SpaceObject Икс, SpaceObject у) => (Икс, у) переключатель        {            _ когда Икс.Размер > 100 && у.Размер > 100 => "биг-бум",             _ => Сталкиваются с(Икс так как динамичный, у так как динамичный)  // Динамическая отправка методу CollideWith        };                // C # не поддерживает глобальные функции. Методы класса - единственный способ реализовать        // Функции CollideWith. Вы можете определить их как неприватные статические методы в        // отдельный класс и используйте директиву using static для ссылки на них, как если бы они         // были глобальными. Это не потребует никаких изменений в методе Collide, описанном выше.        частный статический нить Сталкиваются с(Астероид Икс, Астероид у) => "а / а";        частный статический нить Сталкиваются с(Астероид Икс, Космический корабль у) => "в качестве";        частный статический нить Сталкиваются с(Космический корабль Икс, Астероид у) => "s / a";        частный статический нить Сталкиваются с(Космический корабль Икс, Космический корабль у) => "SS";    }    Абстрактные класс SpaceObject    {        общественный SpaceObject(int размер)        {            Размер = размер;        }        общественный int Размер { получать; }    }    класс Астероид : SpaceObject    {        общественный Астероид(int размер) : основание(размер) { }    }    класс Космический корабль : SpaceObject    {        общественный Космический корабль(int размер) : основание(размер) { }    }

Выход:

большая стрела / ss / s

Groovy

Groovy общего назначения Ява совместимый / многоразовый JVM язык, который, в отличие от Java, использует позднее связывание / множественную отправку.[12]

	/*Groovy реализация примера выше на C #Позднее связывание работает одинаково при использовании нестатических методов или статической компиляции классов / методов.(Аннотация @CompileStatic) 	*/	класс Программа {		статический пустота основной(Строка[] аргументы) {			println Коллайдер.столкнуться(новый Астероид(101), новый Космический корабль(300))			println Коллайдер.столкнуться(новый Астероид(10), новый Космический корабль(10))			println Коллайдер.столкнуться(новый Космический корабль(101), новый Космический корабль(10))		}	}	класс Коллайдер {		статический Строка столкнуться(SpaceObject Икс, SpaceObject у) {			(Икс.размер > 100 && у.размер > 100) ? "биг-бум" : сталкиваются с(Икс, у)  // Динамическая отправка в метод collideWith		}		частный статический Строка сталкиваются с(Астероид Икс, Астероид у) { "а / а" }		частный статический Строка сталкиваются с(Астероид Икс, Космический корабль у) { "в качестве" }		частный статический Строка сталкиваются с(Космический корабль Икс, Астероид у) { "s / a" }		частный статический Строка сталкиваются с(Космический корабль Икс, Космический корабль у) { "SS"}	}	класс SpaceObject {		int размер		SpaceObject(int размер) { это.размер = размер }	}	@InheritConstructors класс Астероид расширяет SpaceObject {}	@InheritConstructors класс Космический корабль расширяет SpaceObject {}

Common Lisp

На языке с множественной отправкой, например Common Lisp, это может выглядеть примерно так (показан пример Common Lisp):

(defmethod сталкиваются с ((Икс астероид) (у астероид))  ;; справиться с астероидом, поражающим астероид  )(defmethod сталкиваются с ((Икс астероид) (у космический корабль))  ;; справиться с столкновением с астероидом космического корабля  )(defmethod сталкиваются с ((Икс космический корабль) (у астероид))  ;; справиться с столкновением космического корабля с астероидом  )(defmethod сталкиваются с ((Икс космический корабль) (у космический корабль))  ;; иметь дело с космическим кораблем, поражающим космический корабль  )

и аналогично для других методов. Явное тестирование и «динамическое приведение» не используются.

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

Юля

Юля имеет встроенную множественную отправку, и это центральное место в языковом дизайне.[3] Версия для Юлии из приведенного выше примера может выглядеть так:

сталкиваются с(Икс::Астероид, у::Астероид) = ... # справиться с столкновением астероида с астероидомсталкиваются с(Икс::Астероид, у::Космический корабль) = ... # справиться с столкновением с астероидом космического кораблясталкиваются с(Икс::Космический корабль, у::Астероид) = ... # справиться с столкновением космического корабля с астероидомсталкиваются с(Икс::Космический корабль, у::Космический корабль) = ... # справиться с столкновением космического корабля

Оболочка нового поколения

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

Методы с одинаковыми именами образуют метод множественной отправки, поэтому специального объявления не требуется.

При вызове метода множественной отправки поиск метода-кандидата выполняется снизу вверх. Когда типы аргументов соответствуют типам, указанным для параметров, вызывается метод. Это контрастирует со многими другими языками, где побеждает наиболее специфичное для типа совпадение. Внутри вызванного метода отказавший защитник (где условие защиты оценивается как ложное) вызывает продолжение поиска метода, вызываемого.

{	тип SpaceObject	тип Астероид(SpaceObject)	тип Космический корабль(SpaceObject)}F в этом(о:SpaceObject, размер:Int) о.размер = размерF столкнуться(Икс:Астероид, у:Астероид) "а / а"F столкнуться(Икс:Астероид, у:Космический корабль) "в качестве"F столкнуться(Икс:Космический корабль, у:Астероид) "s / a"F столкнуться(Икс:Космический корабль, у:Космический корабль) "SS"F столкнуться(Икс:SpaceObject, у:SpaceObject) {	сторожить Икс.размер > 100	сторожить у.размер > 100	"биг-бум"}эхо(столкнуться(Астероид(101), Космический корабль(300)))эхо(столкнуться(Астероид(10), Космический корабль(10)))

Выход:

большой бум / с

Раку

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

В нем есть как мультиметоды, так и мультисабы. Поскольку большинство операторов являются подпрограммами, у него также есть несколько отправленных операторов.

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

подмножество Масса из Настоящий где 0 ^..^ Inf; роль Звездный объект {    имеет Масса $ .mass является требуется;    метод имя () возвращается Ул. {...};}класс Астероид делает Звездный объект {    метод имя () { 'астероид' }}класс Космический корабль делает Звездный объект {    имеет Ул. $ .name = "какой-то безымянный космический корабль";}мой Ул. @destroyed = < стертый уничтожен искалеченный >;мой Ул. @ испорченный = « поврежден 'столкнулся с' 'был поврежден' »;# Мы добавляем несколько кандидатов в числовые операторы сравнения, потому что мы сравниваем их численно,# но нет смысла приводить объекты к числовому типу.# (Если бы они выполняли принуждение, нам не обязательно было бы добавлять эти операторы.)# Таким же образом мы могли бы определить совершенно новые операторы.мульти суб инфикс:« <=> » ( Звездный объект: D $ а, Звездный объект: D $ млрд ) { $ а.масса <=> $ млрд.масса }мульти суб инфикс:« < » ( Звездный объект: D $ а, Звездный объект: D $ млрд ) { $ а.масса < $ млрд.масса }мульти суб инфикс:« > » ( Звездный объект: D $ а, Звездный объект: D $ млрд ) { $ а.масса > $ млрд.масса }мульти суб инфикс:« == » ( Звездный объект: D $ а, Звездный объект: D $ млрд ) { $ а.масса == $ млрд.масса }# Определите новый мульти-диспетчер и добавьте некоторые ограничения типа к параметрам.# Если бы мы не определили его, мы получили бы общий, не имеющий ограничений.прото суб столкнуться ( Звездный объект: D $, Звездный объект: D $ ) {*}# Здесь нет необходимости повторять типы, поскольку они такие же, как и у прототипа.# Ограничение 'where' технически применяется только к $ b, а не ко всей подписи.# Обратите внимание, что ограничение 'where' использует кандидат оператора `<`, который мы добавили ранее.мульти суб столкнуться ( $ а, $ млрд где $ а < $ млрд ) {    сказать "$ a.name () было @ destroy.pick () $ b.name ()";}мульти суб столкнуться ( $ а, $ млрд где $ а > $ млрд ) {    # переадресация к предыдущему кандидату с замененными аргументами    то же самое с $ млрд, $ а;}# Это должно быть после первых двух, потому что остальные# имеют ограничения 'where', которые проверяются в# порядок написания подписок. (Этот всегда будет совпадать.)мульти суб столкнуться ( $ а, $ млрд ) {    # рандомизируем порядок    мой ($ n1, $ n2) = ( $ а.имя, $ млрд.имя ).выбирать(*);    сказать "$ n1 @ Damaged.pick () $ n2";}# Следующие два кандидата могут быть где угодно после прототипа,# потому что они имеют более специализированные типы, чем предыдущие три.# Если корабли имеют неравную массу, вместо них вызывается один из первых двух кандидатов.мульти суб столкнуться ( Космический корабль $ а, Космический корабль $ млрд где $ а == $ млрд ){    мой ($ n1, $ n2) = ( $ а.имя, $ млрд.имя ).выбирать(*);    сказать "$ n1 столкнулся с $ n2, и оба корабля были",    ( @destroyed.выбирать, 'оставлено поврежденным' ).выбирать;}# Вы можете распаковать атрибуты в переменные внутри подписи.# У вас может быть даже ограничение на них `(: mass ($ a) where 10)`.мульти суб столкнуться ( Астероид $ (:масса($ а)), Астероид $ (:масса($ млрд)) ){    сказать "два астероида столкнулись и объединились в один более крупный астероид массы {$ a + $ b}";}мой Космический корабль $ Enterprise .= новый(:масса(1),:имя('Предприятие'));столкнуться Астероид.новый(:масса(.1)), $ Enterprise;столкнуться $ Enterprise, Космический корабль.новый(:масса(.1));столкнуться $ Enterprise, Астероид.новый(:масса(1));столкнуться $ Enterprise, Космический корабль.новый(:масса(1));столкнуться Астероид.новый(:масса(10)), Астероид.новый(:масса(5));

Расширение языков с помощью нескольких библиотек диспетчеризации

JavaScript

В языках, которые не поддерживают множественную отправку на уровне определения языка или синтаксическом уровне, часто можно добавить множественную отправку с помощью библиотека расширение. JavaScript и TypeScript не поддерживают мультиметоды на уровне синтаксиса, но можно добавить множественную отправку через библиотеку. Например, мультиметодный пакет[14] обеспечивает реализацию универсальных функций с множественной отправкой.

Версия с динамической типизацией в JavaScript:

импорт { мульти, метод } из '@ стрелки / мультиметод'класс Астероид {}класс Космический корабль {}const сталкиваются с = мульти(  метод([Астероид, Астероид], (Икс, у) => {    // справляемся с астероидом, поражающим астероид  }),  метод([Астероид, Космический корабль], (Икс, у) => {    // справиться с столкновением с астероидом космического корабля  }),  метод([Космический корабль, Астероид], (Икс, у) => {    // справиться с столкновением космического корабля с астероидом  }),  метод([Космический корабль, Космический корабль], (Икс, у) => {    // дело с космическим кораблем, поражающим космический корабль  }),)

Статически типизированная версия в TypeScript:

импорт { мульти, метод, Мульти } из '@ стрелки / мультиметод'класс Астероид {}класс Космический корабль {}тип Сталкиваются с = Мульти & {  (Икс: Астероид, у: Астероид): пустота  (Икс: Астероид, у: Космический корабль): пустота  (Икс: Космический корабль, у: Астероид): пустота  (Икс: Космический корабль, у: Космический корабль): пустота}const сталкиваются с: Сталкиваются с = мульти(  метод([Астероид, Астероид], (Икс, у) => {    // справляемся с астероидом, поражающим астероид  }),  метод([Астероид, Космический корабль], (Икс, у) => {    // справиться с столкновением с астероидом космического корабля  }),  метод([Космический корабль, Астероид], (Икс, у) => {    // справиться с столкновением космического корабля с астероидом  }),  метод([Космический корабль, Космический корабль], (Икс, у) => {    // дело с космическим кораблем, поражающим космический корабль  }),)

Python

Множественная отправка может быть добавлена ​​к Python с помощью библиотека расширение. Например, модуль multimethods.py[15] предоставляет мультиметоды в стиле CLOS для Python без изменения базового синтаксиса или ключевых слов языка.

из мультиметоды импорт Отправлятьиз game_objects импорт Астероид, Космический корабльиз game_behaviors импорт as_func, ss_func, sa_funcстолкнуться = Отправлять()столкнуться.add_rule((Астероид, Космический корабль), as_func)столкнуться.add_rule((Космический корабль, Космический корабль), ss_func)столкнуться.add_rule((Космический корабль, Астероид), sa_func)def aa_func(а, б):    "" "Поведение при столкновении астероида с астероидом." ""    # ... определить новое поведение ...столкнуться.add_rule((Астероид, Астероид), aa_func)
# ...потом...столкнуться(вещь 1, вещь2)

Функционально это очень похоже на пример CLOS, но синтаксис - обычный Python.

Использование Python 2.4 декораторы, Гвидо ван Россум произвел образец реализации мультиметодов[16] с упрощенным синтаксисом:

@multimethod(Астероид, Астероид)def столкнуться(а, б):    "" "Поведение при столкновении астероида с астероидом." ""    # ... определить новое поведение ...@multimethod(Астероид, Космический корабль)def столкнуться(а, б):    "" "Поведение при столкновении астероида с космическим кораблем." ""    # ... определить новое поведение ...# ... определить другие правила для нескольких методов ...

а затем переходит к определению мультиметодного декоратора.

Пакет PEAK-Rules обеспечивает множественную отправку с синтаксисом, аналогичным приведенному выше примеру.[17] Позже он был заменен PyProtocols.[18]

Библиотека Reg также поддерживает множественную и предикатную отправку.[19]

Эмуляция множественной отправки

C

C не имеет динамической диспетчеризации, поэтому ее необходимо реализовать вручную в той или иной форме. Часто перечисление используется для определения подтипа объекта. Динамическую отправку можно выполнить, просмотрев это значение в указатель на функцию разделительный стол. Вот простой пример на C:

typedef пустота (*CollisionCase)(пустота);пустота collision_AA(пустота) { / * обрабатываем столкновение астероид-астероид * / };пустота collision_AS(пустота) { / * обрабатываем столкновение астероид-космический корабль * / };пустота collision_SA(пустота) { / * обрабатываем столкновение космического корабля и астероида * / };пустота collision_SS(пустота) { / * обрабатываем столкновение космического корабля с космическим кораблем * / };typedef перечислить {    THING_ASTEROID = 0,    THING_SPACESHIP,    THING_COUNT / * не тип самого предмета, вместо этого используется для поиска количества вещей * /} Вещь;CollisionCase столкновения[THING_COUNT][THING_COUNT] = {    {&collision_AA, &collision_AS},    {&collision_SA, &collision_SS}};пустота столкнуться(Вещь а, Вещь б) {    (*столкновения[а][б])();}int основной(пустота) {    столкнуться(THING_SPACESHIP, THING_ASTEROID);}

С помощью библиотеки C Object System[20] C поддерживает динамическую отправку, аналогичную CLOS. Он полностью расширяемый и не требует ручной обработки методов. Динамические сообщения (методы) отправляются диспетчером COS, который быстрее, чем Objective-C. Вот пример в COS:

#включают <stdio.h>#включают <cos/Object.h>#включают <cos/gen/object.h>// классыdefclass (Астероид)// члены данныхконец классаdefclass (Космический корабль)// члены данныхконец класса// дженерикиdefgeneric (_Bool, сталкиваются с, _1, _2);// мультиметодыdefmethod (_Bool, сталкиваются с, Астероид, Астероид) // справляемся с астероидом, поражающим астероидendmethoddefmethod (_Bool, сталкиваются с, Астероид, Космический корабль) // справиться с столкновением с астероидом космического корабляendmethoddefmethod (_Bool, сталкиваются с, Космический корабль, Астероид) // справиться с столкновением космического корабля с астероидомendmethoddefmethod (_Bool, сталкиваются с, Космический корабль, Космический корабль) // дело с космическим кораблем, поражающим космический корабльendmethod// пример использованияint основной(пустота){  OBJ а = gnew(Астероид);  OBJ s = gnew(Космический корабль);  printf(" =% d п", сталкиваются с(а, а));  printf(" =% d п", сталкиваются с(а, s));  printf(" =% d п", сталкиваются с(s, а));  printf(" =% d п", сталкиваются с(s, s));  Grelease(а);  Grelease(s);}

C ++

По состоянию на 2018 год, C ++ изначально поддерживает только одиночную отправку, хотя рассматривается возможность добавления множественной отправки.[21] Методы работы с этим пределом аналогичны: используйте либо шаблон посетителя, динамическое приведение или библиотека:

 // Пример использования сравнения типов времени выполнения через dynamic_cast структура Вещь {     виртуальный пустота сталкиваются с(Вещь& Другой) = 0; }; структура Астероид : Вещь {     пустота сталкиваются с(Вещь& Другой) {         // dynamic_cast к типу указателя возвращает NULL, если приведение не удалось         // (dynamic_cast к ссылочному типу вызовет исключение при ошибке)         если (авто астероид = dynamic_cast<Астероид*>(&Другой)) {             // обрабатываем столкновение астероид-астероид         } еще если (авто космический корабль = dynamic_cast<Космический корабль*>(&Другой)) {             // обрабатываем столкновение астероид-космический корабль         } еще {             // здесь обработка столкновений по умолчанию         }     } }; структура Космический корабль : Вещь {     пустота сталкиваются с(Вещь& Другой) {         если (авто астероид = dynamic_cast<Астероид*>(&Другой)) {             // обрабатываем столкновение космического корабля и астероида         } еще если (авто космический корабль = dynamic_cast<Космический корабль*>(&Другой)) {             // обрабатываем столкновение космического корабля с космическим кораблем         } еще {             // здесь обработка столкновений по умолчанию         }     } };

или таблица поиска указателя на метод:

#включают <cstdint>#включают <typeinfo>#включают <unordered_map>класс Вещь {  защищенный:    Вещь(стандартное::uint32_t Сид) : tid(Сид) {}    const стандартное::uint32_t tid; // тип id    typedef пустота (Вещь::*CollisionHandler)(Вещь& Другой);    typedef стандартное::unordered_map<стандартное::uint64_t, CollisionHandler> CollisionHandlerMap;    статический пустота addHandler(стандартное::uint32_t id1, стандартное::uint32_t id2, CollisionHandler обработчик) {        столкновения.вставить(CollisionHandlerMap::тип ценности(ключ(id1, id2), обработчик));    }    статический стандартное::uint64_t ключ(стандартное::uint32_t id1, стандартное::uint32_t id2) {        вернуть стандартное::uint64_t(id1) << 32 | id2;    }    статический CollisionHandlerMap столкновения;  общественный:    пустота сталкиваются с(Вещь& Другой) {        авто обработчик = столкновения.найти(ключ(tid, Другой.tid));        если (обработчик != столкновения.конец()) {            (это->*обработчик->второй)(Другой); // вызов указателя на метод        } еще {            // обработка столкновений по умолчанию        }    }};класс Астероид: общественный Вещь {    пустота asteroid_collision(Вещь& Другой)   { / * обрабатываем столкновение астероид-астероид * / }    пустота космический корабль_коллизия(Вещь& Другой)  { / * обрабатываем столкновение астероид-космический корабль * /}  общественный:    Астероид(): Вещь(Сид) {}    статический пустота initCases();    статический const стандартное::uint32_t Сид;};класс Космический корабль: общественный Вещь {    пустота asteroid_collision(Вещь& Другой)   { / * обрабатываем столкновение космического корабля и астероида * /}    пустота космический корабль_коллизия(Вещь& Другой)  { / * обрабатываем столкновение космического корабля с космическим кораблем * /}  общественный:    Космический корабль(): Вещь(Сид) {}    статический пустота initCases();    статический const стандартное::uint32_t Сид; // ID класса};Вещь::CollisionHandlerMap Вещь::столкновения;const стандартное::uint32_t Астероид::Сид = типичный(Астероид).хэш-код();const стандартное::uint32_t Космический корабль::Сид = типичный(Космический корабль).хэш-код();пустота Астероид::initCases() {    addHandler(Сид, Сид, CollisionHandler(&Астероид::asteroid_collision));    addHandler(Сид, Космический корабль::Сид, CollisionHandler(&Астероид::космический корабль_коллизия));}пустота Космический корабль::initCases() {    addHandler(Сид, Астероид::Сид, CollisionHandler(&Космический корабль::asteroid_collision));    addHandler(Сид, Сид, CollisionHandler(&Космический корабль::космический корабль_коллизия));}int основной() {    Астероид::initCases();    Космический корабль::initCases();    Астероид  а1, а2;    Космический корабль s1, s2;    а1.сталкиваются с(а2);    а1.сталкиваются с(s1);    s1.сталкиваются с(s2);    s1.сталкиваются с(а1);}

В йомм2 библиотека[22] обеспечивает быструю ортогональную реализацию открытых мультиметодов.

Синтаксис объявления открытых методов основан на предложении о реализации на языке C ++. Библиотека требует, чтобы пользователь зарегистрировал все классы, используемые в качестве виртуальных аргументов (и их подклассы), но не требует каких-либо изменений существующего кода. Методы реализованы как обычные встроенные функции C ++; они могут быть перегружены и могут быть пропущены указателем. Нет ограничений на количество виртуальных аргументов, и они могут быть произвольно смешаны с невиртуальными аргументами.

Библиотека использует комбинацию методов (сжатые таблицы диспетчеризации, идеальный целочисленный хэш) для реализации вызовов методов в постоянное время, уменьшая использование памяти. Отправка вызова открытого метода с одним виртуальным аргументом занимает всего на 15–30% больше времени, чем вызов обычной функции виртуального члена, когда используется современный оптимизирующий компилятор.

Пример Asteroids можно реализовать следующим образом:

#включают <yorel/yomm2/cute.hpp>с помощью Йорел::йомм2::virtual_;класс Вещь {  общественный:    виртуальный ~Вещь() {}    // ...};класс Астероид : общественный Вещь {    // ...};класс Космический корабль : общественный Вещь {    // ...};register_class(Вещь);register_class(Космический корабль, Вещь);register_class(Астероид, Вещь);declare_method(пустота, сталкиваются с, (virtual_<Вещь&>, virtual_<Вещь&>));define_method(пустота, сталкиваются с, (Вещь& оставили, Вещь& верно)) {    // обработка столкновений по умолчанию}define_method(пустота, сталкиваются с, (Астероид& оставили, Астероид& верно)) {    // обрабатываем столкновение астероид-астероид}define_method(пустота, сталкиваются с, (Астероид& оставили, Космический корабль& верно)) {    // обрабатываем столкновение астероид-космический корабль}define_method(пустота, сталкиваются с, (Космический корабль& оставили, Астероид& верно)) {    // обрабатываем столкновение космического корабля и астероида}define_method(пустота, сталкиваются с, (Космический корабль& оставили, Космический корабль& верно)) {    // обрабатываем столкновение космического корабля с космическим кораблем}int основной() {    Йорел::йомм2::update_methods();    Астероид  а1, а2;    Космический корабль s1, s2;    сталкиваются с(а1, а2);    сталкиваются с(а1, s1);    сталкиваются с(s1, s2);    сталкиваются с(s1, а1);}

Страуструп упоминает в Дизайн и эволюция C ++ что ему нравится концепция мультиметодов, и он рассматривает возможность ее реализации на C ++, но утверждает, что не смог найти эффективный образец реализации (сопоставимый с виртуальными функциями) и решить некоторые возможные проблемы неоднозначности типов. Затем он заявляет, что, хотя эту функцию все же было бы неплохо иметь, ее можно приблизительно реализовать с помощью двойная отправка или таблица поиска на основе типов, как показано в примере C / C ++ выше, поэтому это функция с низким приоритетом для будущих версий языка.[23]

D

По состоянию на 2018 год, как и многие другие объектно-ориентированные языки программирования, D изначально поддерживает только однократную отправку. Однако можно эмулировать открытые мультиметоды как библиотечную функцию в D. открытые методы библиотека[24] это пример.

// ОбъявлениеМатрица плюс(виртуальный!Матрица, виртуальный!Матрица);// Переопределение для двух объектов DenseMatrix@methodМатрица _plus(DenseMatrix а, DenseMatrix б){  const int номер = а.ряды;  const int NC = а.cols;  утверждать(а.номер == б.номер);  утверждать(а.NC == б.NC);  авто результат = новый DenseMatrix;  результат.номер = номер;  результат.NC = NC;  результат.Elems.длина = а.Elems.длина;  результат.Elems[] = а.Elems[] + б.Elems[];  вернуть результат;}// Переопределение для двух объектов DiagonalMatrix@methodМатрица _plus(ДиагональМатрица а, ДиагональМатрица б){  утверждать(а.ряды == б.ряды);  двойной[] сумма;  сумма.длина = а.Elems.длина;  сумма[] = а.Elems[] + б.Elems[];  вернуть новый ДиагональМатрица(сумма);}

Ява

На языке с единственной отправкой, например Ява, множественная отправка может быть эмулирована с несколькими уровнями единой отправки:

интерфейс Столкновение {    пустота сталкиваются с(окончательный Столкновение Другой);    / * Этим методам потребуются разные имена на языке без перегрузки методов. * /    пустота сталкиваются с(окончательный Астероид астероид);    пустота сталкиваются с(окончательный Космический корабль космический корабль);}класс Астероид орудия Столкновение {    общественный пустота сталкиваются с(окончательный Столкновение Другой) {        // Вызов collideWith для другого объекта.        Другой.сталкиваются с(это);   }    общественный пустота сталкиваются с(окончательный Астероид астероид) {        // Обработка столкновения астероид-астероид.    }    общественный пустота сталкиваются с(окончательный Космический корабль космический корабль) {        // Обработка столкновения астероида и космического корабля.    }}класс Космический корабль орудия Столкновение {    общественный пустота сталкиваются с(окончательный Столкновение Другой) {        // Вызов collideWith для другого объекта.        Другой.сталкиваются с(это);    }    общественный пустота сталкиваются с(окончательный Астероид астероид) {        // Обработка столкновения космического корабля и астероида.    }    общественный пустота сталкиваются с(окончательный Космический корабль космический корабль) {        // Обработка столкновения космического корабля с космическим кораблем.    }}

Время выполнения экземпляр Также можно использовать проверки на одном или обоих уровнях.

Поддержка языков программирования

Первичная парадигма

Поддержка общих мультиметодов

Через расширения

Смотрите также

Рекомендации

  1. ^ Ранка, Санджай; Банерджи, Арунава; Бисвас, Канад Кишор; Дуа, Сумит; Мишра, Прабхат; Муна, Раджат (26.07.2010). Современные вычисления: Вторая международная конференция, IC3 2010, Нойда, Индия, 9–11 августа 2010 г. Протоколы. Springer. ISBN  9783642148248.
  2. ^ а б c d е ж грамм час я j k Мушевичи, Раду; Потанин Алексей; Темперо, Эван; Благородный, Джеймс (2008). Множественная отправка на практике. Материалы 23-й конференции ACM SIGPLAN по языкам и приложениям систем объектно-ориентированного программирования. OOPSLA '08. Нэшвилл, Теннесси, США: ACM. С. 563–582. Дои:10.1145/1449764.1449808. ISBN  9781605582153. S2CID  7605233.
  3. ^ а б c d е Безансон, Джефф; Эдельман, Алан; Карпинский, Стефан; Шах, Вирал Б. (7 февраля 2017 г.). «Юлия: свежий подход к численным вычислениям». SIAM Обзор. 59 (1): 65–98. arXiv:1411.1607. Дои:10.1137/141000671. S2CID  13026838.
  4. ^ Кастанья, Джузеппе; Гелли, Джорджио и Лонго, Джузеппе (1995). «Расчет для перегруженных функций с подтипами». Информация и вычисления. 117 (1): 115–135. Дои:10.1006 / inco.1995.1033. Получено 2013-04-19.
  5. ^ Кастанья, Джузеппе (1996). Объектно-ориентированное программирование: единый фундамент. Успехи теоретической информатики. Birkhäuser. п. 384. ISBN  978-0-8176-3905-1.
  6. ^ Кастанья, Джузеппе (1995). «Ковариация и контравариантность: конфликт без причины». Транзакции ACM по языкам и системам программирования. 17 (3): 431–447. CiteSeerX  10.1.1.115.5992. Дои:10.1145/203095.203096. S2CID  15402223.
  7. ^ Брюс, Ким; Карделли, Лука; Кастанья, Джузеппе; Ливенс, Гэри Т .; Пирс, Бенджамин (1995). «О бинарных методах». Теория и практика объектных систем. 1 (3): 221–242. Дои:10.1002 / j.1096-9942.1995.tb00019.x. Получено 2013-04-19.
  8. ^ «Использование динамического типа (Руководство по программированию на C #)». Получено 2020-05-14.
  9. ^ "выражение переключения (справочник по C #)". Получено 2020-05-14.
  10. ^ "Базовые концепты". Получено 2020-05-14.
  11. ^ «Dynamic .NET - понимание динамического ключевого слова в C # 4». Получено 2020-05-14.
  12. ^ Groovy - мульти-методы
  13. ^ "Руководство пользователя NGSLANG (1) NGS". ngs-lang.org. Получено 2019-10-01.
  14. ^ @ стрелки / мультиметод Множественная отправка в JavaScript / TypeScript с настраиваемым разрешением отправки от Maciej Cąderek.
  15. ^ multimethods.py В архиве 2005-03-09 на Wayback Machine, Множественная отправка в Python с настраиваемым разрешением отправки, Дэвид Мертц и др.
  16. ^ «Пятиминутные мультиметоды на Python».
  17. ^ "ПИК-Правила 0.5a1.dev". Индекс пакета Python. Получено 21 марта 2014.
  18. ^ "PyProtocols". Комплект приложений Python Enterprise. Получено 26 апреля 2019.
  19. ^ "Рег". Читать документы. Получено 26 апреля 2019.
  20. ^ «C Object System: структура, которая выводит C на уровень других языков программирования высокого уровня и выше: CObjectSystem / COS». 2019-02-19.
  21. ^ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2216.pdf
  22. ^ йомм2, Быстрые, ортогональные открытые мульти-методы для C ++, Жан-Луи Леруа.
  23. ^ Страуструп, Бьярн (1994). «Раздел 13.8». Дизайн и эволюция C ++. Индианаполис, Индиана, США: Аддисон Уэсли. Bibcode:1994dec..book ..... S. ISBN  978-0-201-54330-8.
  24. ^ открытые методы, Открытые мульти-методы для D Жан-Луи Леруа.
  25. ^ «Методы». Руководство Джулии. Джулиаланг. Архивировано из оригинал 17 июля 2016 г.. Получено 11 мая 2014.
  26. ^ "Мультиметоды в C # 4.0 с 'Dynamic'". Получено 2009-08-20.
  27. ^ "Сесил язык". Получено 2008-04-13.
  28. ^ "Мультиметоды в Clojure". Получено 2008-09-04.
  29. ^ Стил, Гай Л. (1990). "28". Общий LISP: язык. Бедфорд, Массачусетс, США: Digital Press. ISBN  978-1-55558-041-4.
  30. ^ «Предпосылки и цели». Получено 2008-04-13.
  31. ^ "Elixir Lang | Начало работы | Модули и функции". Получено 2017-11-10.
  32. ^ "Спецификация языка Fortress, версия 1.0" (PDF). Архивировано из оригинал (PDF) на 2013-01-20. Получено 2010-04-23.
  33. ^ "Мультиметоды в Groovy". Получено 2008-04-13.
  34. ^ «Методы - LassoGuide 9.2». Получено 2014-11-11.
  35. ^ «Шаблон посетителя против нескольких методов». Получено 2008-04-13.
  36. ^ «Руководство Nim: несколько методов». Получено 2020-09-11.
  37. ^ "Часто задаваемые вопросы о Perl 6". Получено 2008-04-13.
  38. ^ «Как работают методы S4» (PDF). Получено 2008-04-13.
  39. ^ "Множественная отправка в Seed7". Получено 2011-04-23.
  40. ^ «Системное руководство TADS 3». Получено 2012-03-19.
  41. ^ "Множественная рассылка VB.Net". Получено 2020-03-31.
  42. ^ «Новые возможности в C # 4.0 и VB.Net 10.0». Получено 2020-03-31.
  43. ^ «Заметки для экспертов по языку программирования». Получено 2016-08-21.
  44. ^ «Множественная отправка».

внешняя ссылка