stringtranslate.com

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

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

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

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

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

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

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

Объектная система Common Lisp (CLOS) — ранний и хорошо известный пример множественной диспетчеризации. Еще одним ярким примером использования множественной диспетчеризации является язык программирования Julia .

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

Типы данных

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

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

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

Проблемы

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

Выразительность и модульность

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

Так называемая проблема выражения связана с возможностью кода в зависимом пакете расширять поведение (функции или типы данных), определенные в базовом пакете, из включающего пакета, не изменяя исходный код базового пакета. Традиционные объектно-ориентированные языки с единой диспетчеризацией упрощают добавление новых типов данных, но не новых функций; традиционные функциональные языки имеют тенденцию иметь противоположный эффект, а множественная диспетчеризация, если она реализована правильно, позволяет и то, и другое. Желательно, чтобы реализация множественной диспетчеризации имела следующие свойства:

Двусмысленность

Обычно желательно, чтобы для любого данного вызова мультиметода среди случаев реализации мультиметода был не более одного «лучшего» кандидата, и/или чтобы, если такового нет, этот вопрос решался предсказуемым и предсказуемым образом. детерминистический способ, включая неудачу. Недетерминированное поведение нежелательно. Предполагая набор типов с нециклической связью подтипов, можно определить, что одна реализация мультиметода является «лучшей» (более конкретной), если все динамически отправляемые аргументы в первом являются подтипами всех указанных динамически отправляемых аргументов. во втором, и по крайней мере один из них является строгим подтипом. При одиночной отправке и отсутствии множественного наследования это условие тривиально выполняется, но при множественной отправке два или более кандидатов могут удовлетворять заданному фактическому списку аргументов, но ни один из них не является более конкретным, чем другой (один динамический аргумент в одном случае это подтип, а в другом — подтип). Это, в частности, может произойти, если два разных пакета, ни один из которых не зависит от другого, расширяют некоторый мультиметод реализациями, относящимися к типам каждого пакета, а затем третий пакет, который включает оба (возможно, косвенно), затем вызывает мультиметод, используя аргументы из обоих. пакеты.

Возможные решения включают в себя:

Эффективность

Эффективная реализация единой диспетчеризации, в том числе на языках программирования, которые отдельно компилируются в объектный код и связываются с низкоуровневым (не поддерживающим язык) компоновщиком, в том числе динамически во время загрузки/запуска программы или даже под управлением приложения. код, хорошо известны. Метод " vtable ", разработанный на C++ и других ранних объектно-ориентированных языках (где каждый класс имеет массив указателей функций, соответствующих виртуальным функциям этого класса), почти так же быстр, как вызов статического метода, требуя накладных расходов O(1) и только одного дополнительного поиск в памяти даже в неоптимизированном случае. Однако метод vtable использует имя функции, а не тип аргумента, в качестве ключа поиска и не масштабируется для случая множественной отправки. (Это также зависит от объектно-ориентированной парадигмы методов, которые являются функциями классов, а не отдельными объектами, независимыми от какого-либо конкретного типа данных).

Эффективная реализация множественной диспетчеризации остается постоянной исследовательской проблемой.

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

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

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

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

Теория

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

Примеры

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

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

С#

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

В приведенном ниже примере используются функции, представленные в C# 9 и C# 10.

использование статической ColliderLibrary ;  Консоль . WriteLine ( Collide ( новый астероид ( 101 ), новый космический корабль ( 300 ))); Консоль . WriteLine ( Collide ( новый астероид ( 10 ), новый космический корабль ( 10 ))); Консоль . WriteLine ( Collide ( новый космический корабль ( 101 ), новый космический корабль ( 10 )));         строка Столкновение ( SpaceObject x , SpaceObject y ) => x . Размер > 100 && y . Размер > 100 ? «Большой бум!» : CollideWith ( x как динамический , y как динамический ); // Динамическая отправка методу CollideWith                      класс ColliderLibrary { публичная статическая строка CollideWith ( Астероид x , Астероид y ) => "a/a" ; общедоступная статическая строка CollideWith ( Астероид x , Космический корабль y ) => "a/s" ; public static string CollideWith ( Космический корабль x , Астероид y ) => "s/a" ; общедоступная статическая строка CollideWith ( Космический корабль x , Космический корабль y ) => "s/s" ; }                                     абстрактная запись SpaceObject ( int Size ); запись Asteroid ( int Size ) : SpaceObject ( Size ); запись Космический корабль ( int Size ) : SpaceObject ( Size );           

Выход:

Большой бум! а/с с/с

классный

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

/*  Groovy-реализация приведенного выше примера C#  Позднее связывание работает одинаково при использовании нестатических методов или статической компиляции класса/методов  (@CompileStatic annotation) */ class Program { static void main ( String [] args ) { println Collider . столкновение ( новый астероид ( 101 ), новый космический корабль ( 300 )) println Collider . столкновение ( новый астероид ( 10 ), новый космический корабль ( 10 )) println Collider . столкновение ( новый космический корабль ( 101 ), новый космический корабль ( 10 )) } }                       class Collider { static String столкновение ( SpaceObject x , SpaceObject y ) { ( x . size > 100 && y . size > 100 ) ? "big-boom" : collideWith ( x , y ) // Динамическая отправка методуcollideWith }                        Private static StringcollideWith ( Asteroid x , Asteroid y ) { " a /a" } Private static StringcollideWith ( Asteroid x , Spaceship y ) { " a/s" } Private static StringcollideWith ( Spaceship x , Asteroid y ) { "s /a" } Private static StringcollideWith ( Космический корабль x , Космический корабль y ) { " s / s" } }                                      класс SpaceObject { размер int SpaceObject ( размер int ) { this . размер = размер } }           Класс @InheritConstructors Asteroid расширяет SpaceObject {} Класс @InheritConstructors Spaceship расширяет SpaceObject {}          

Общий Лисп

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

( defmethod столкновение-с (( x астероид ) ( y астероид )) ;; справиться с столкновением астероида с астероидом ) ( defmethod столкновение-с (( x астероид ) ( y космический корабль )) ;; справиться с астероидом, столкнувшимся с космическим кораблем ) ( defmethodcollide- with (( x spaceship ) ( y asteroid )) ;; справиться с столкновением космического корабля с астероидом ) ( defmethodcollide -with (( x spaceship ) ( y spaceship )) ;; справиться с столкновением космического корабля с космическим кораблем )                            

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

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

Юлия

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

абстрактный тип SpaceObject end   struct  Asteroid <: Размер SpaceObject :: Int end struct Spaceship <: Размер SpaceObject :: Int end         collide_with ( :: Asteroid , :: Spaceship ) = "a/s" collide_with ( :: Spaceship , :: Asteroid ) = "s/a" collide_with ( :: Spaceship , :: Spaceship ) = "s/s" collide_with ( :: Астероид , :: Астероид ) = "а/а"            столкновение ( x :: SpaceObject , y :: SpaceObject ) = ( x . size > 100 && y . size > 100 ) ? «Большой бум!» : collide_with ( x , y )              

Выход:

julia> collide ( Астероид ( 101 ), Космический корабль ( 300 )) "Большой бум!"  julia> collide ( Астероид ( 10 ), Космический корабль ( 10 )) "a/s"  julia> collide ( Космический корабль ( 101 ), Космический корабль ( 10 )) "с/с"  

Раку

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

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

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

подмножество  Mass  of  Real  , где  0 ^..^ Inf ; роль  Звездный-Объект { имеет  массу  Требуется $ .mass  ; имя метода () возвращает Str {...};   }класс  Asteroid  выполняет  Stellar-Object { имя метода () { 'астероид' }}class  Spaceship  do  Stellar-Object { has  Str  $.name = 'какой-то безымянный космический корабль' ;}my  Str  @destroyed = < уничтожено  уничтожено  искалечено >; my  Str  @damaged = «  поврежден  «столкнулся с»  «был поврежден»  »;# Мы добавляем несколько кандидатов к операторам числового сравнения, потому что мы сравниваем их численно, # но нет смысла приводить объекты к числовому типу. # (Если бы они применяли принуждение, нам не обязательно было бы добавлять эти операторы.) # Мы могли бы также определить совершенно новые операторы таким же образом. мульти  -инфикс  : « <=> » ( Stellar-Object:D  $a , Stellar-Object:D  $b ) { $a . масса <=> $b . масса } мульти  субинфикс  : « < » ( Stellar-Object:D  $a , Stellar-Object:D  $b ) { $a . масса < $b . масса } мульти  субинфикс  : « > » ( Stellar-Object:D  $a , Stellar-Object:D  $b ) { $a . масса > $b . масса } мульти  субинфикс  : « == » ( Stellar-Object:D  $a , Stellar-Object:D  $b ) { $a . масса == $b . масса }# Определите новый мультидиспетчер и добавьте к параметрам некоторые ограничения типа. # Если бы мы его не определили, мы бы получили общий вариант без ограничений. протоподстолкновение ( Stellar- Object  :D $, Stellar-Object:D $ ) {* }# Здесь нет необходимости повторять типы, поскольку они такие же, как в прототипе. # Технически ограничение «где» применяется только к $b, а не ко всей подписи. # Обратите внимание, что ограничение 'where' использует кандидата на оператор `<`, который мы добавили ранее. multi  sub  столкновение ( $a , $b  где  $a < $b ) { скажем  "$a.name() было @destroyed.pick() от $b.name()" ;}multi  sub  collide ( $a , $b  где  $a > $b ) { # повторная отправка предыдущему кандидату с заменой аргументов  на  $b , $a ;}# Это должно быть после первых двух, потому что остальные # имеют ограничения "где", которые проверяются в том # порядке, в котором были написаны подпрограммы. (Этот элемент всегда будет совпадать.) multi sub  collide  ( $ a , $b ) { # рандомизировать порядок  my ( $n1 , $n2 ) = ( $a.name , $ b.name ) . выбирать (*); скажите "$n1 @damaged.pick() $n2" ; }# Следующие два кандидата могут находиться где угодно после прототипа, # потому что они имеют более специализированные типы, чем предыдущие три.# Если корабли имеют неравную массу, вместо этого вызывается один из первых двух кандидатов. множественное  столкновение ( Космический корабль $a , Космический корабль $b , где $a == $b ) { my ( $ n1 , $n2 ) = ( $ a.name , $ b.name ) . выбирать (*); скажем "$n1 столкнулся с $n2, и оба корабля были " ,      ( @destroyed . Pick , «осталось поврежденным» ). выбирать ;}# Вы можете распаковать атрибуты в переменные внутри подписи. # Вы могли бы даже установить для них ограничение `(:mass($a)where 10)`. multi  sub  collide ( Asteroid $ (: Mass ( $a ))), Asteroid $ (: Mass ( $b )) ) { скажем  , «два астероида столкнулись и объединились в один больший астероид с массой { $a + $b }» ;}мой  космический корабль  $Enterprise .= new (: mass ( 1 ),: name ( 'The Enterprise' )); столкнуться  с астероидом . новый (: масса ( .1 )), $Enterprise ; столкновение  $Enterprise , Космический корабль . новый (: масса ( .1 )); столкновение  $Enterprise , Астероид . новый (: масса ( 1 )); столкновение  $ Enterprise , Космический корабль . новый (: масса ( 1 )); столкнуться  с астероидом . новый (: масса ( 10 )), Астероид . новый (: масса ( 5 ));

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

JavaScript

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

Динамически типизированная версия на JavaScript:

импортировать { multi , метод } из '@arrows/multimethod'      класс Астероид {} класс Космический корабль {}    constcollideWith = multi ( метод ([ Астероид , Астероид ], ( x , y ) => { // сделка с астероидом, врезающимся в астероид } ), метод ([ Астероид , Космический корабль ], ( x , y ) => { // сделка с столкновением астероида с космическим кораблем }), метод ([ Космический корабль , Астероид ], ( x , y ) => { // имеем дело с столкновением космического корабля с астероидом }), метод ([ Космический корабль , Космический корабль ], ( x , y ) => { / / разобраться с столкновением космического корабля с космическим кораблем }), )                                   

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

импортировать { multi , метод , Multi } из '@arrows/multimethod'       класс Астероид {} класс Космический корабль {}    type CollideWith = Multi & { ( x : Астероид , y : Астероид ) : void ( x : Астероид , y : Космический корабль ) : void ( x : Космический корабль , y : Астероид ) : void ( x : Космический корабль , y : Космический корабль ) : void }                         const collideWith : CollideWith = multi ( метод ([ Asteroid , Asteroid ], ( x , y ) => { // имеем дело с столкновением астероида с астероидом }), метод ([ Asteroid , Spaceship ], ( x , y ) => { / / дело о столкновении астероида с космическим кораблем }), метод ([ Космический корабль , Астероид ], ( x , y ) => { // дело о столкновении космического корабля с астероидом }), метод ([ Космический корабль , космический корабль ], ( x , y ) => { // дело о столкновении космического корабля с космическим кораблем }), )                                    

Питон

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

из  multimethods  import  Dispatch из  game_objects  import  Asteroid ,  Spaceship из  game_behaviors  import  as_func ,  ss_func ,  sa_funcстолкновение  =  Отправка () столкновение . add_rule (( Астероид ,  Космический корабль ),  as_func ) столкновение . add_rule (( Космический корабль ,  Космический корабль ),  ss_func ) столкновение . add_rule (( Космический корабль ,  Астероид ),  sa_func )def  aa_func ( a ,  b ): """Поведение при столкновении астероида с астероидом.""" # ...определить новое поведение...  столкнуться . add_rule (( Астероид ,  Астероид ),  aa_func )
# ...позже... столкнуться ( thing1 ,  thing2 )

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

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

@multimethod ( Asteroid ,  Asteroid ) def  Collide ( a ,  b ): """Поведение при столкновении астероида с астероидом.""" # ...define new Behavior... @multimethod ( Asteroid , Spaceship ) def Collide ( a , b ): """Поведение при столкновении астероида с космическим кораблем.""" # ...определить новое поведение... # ... определить другие правила мультиметода...       

а затем определяется декоратор мультиметода.

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

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

С появлением подсказок типов возможна множественная отправка с еще более простым синтаксисом. Например, используя Plum-Dispatch,

от  импортной отправки сливы  @dispatch defcollide  ( a : Asteroid , b : Asteroid ): """Поведение при столкновении астероида с астероидом.""" # ...определить новое поведение...      @dispatch defcollide  ( a : Asteroid , b : Spaceship ): """Поведение при столкновении астероида с космическим кораблем.""" # ...определить новое поведение...      # ...определить дальнейшие правила...

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

С

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

typedef void ( * CollisionCase )( void );  void columns_AA ( void ) { /* обработка столкновения астероидов с астероидами */ }; void columns_AS ( void ) { /* обрабатываем столкновение астероида и космического корабля */ }; void columns_SA ( void ) { /* обработка столкновения космического корабля с астероидом */ }; void столкновение_SS ( void ) { /* обработка столкновения космических кораблей с космическими кораблями */ };                typedef enum { THING_ASTEROID = 0 , THING_SPACESHIP , THING_COUNT /* не является типом самой вещи, вместо этого используется для поиска количества вещей */ } Thing ;         CollisionCase ColombiaCases [ THING_COUNT ][ THING_COUNT ] = { { & Colombia_AA , & Collision_AS }, { & Collision_SA , & Collision_SS } };       void Collide ( Thing a , Thing b ) { ( * columnsCases [ a ][ b ])(); }      int main ( void ) { столкновение ( THING_SPACESHIP , THING_ASTEROID ); }    

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

#include <stdio.h> #include <cos/Object.h> #include <cos/gen/object.h>   // классыdefclass ( Asteroid ) // члены данных endclass defclass ( Космический корабль ) // члены данных endclass // дженерикиdefgeneric ( _Bool , столкновение_с , _1 , _2 );    // мультиметодыdefmethod ( _Bool , collide_with , Asteroid , Asteroid ) // борьба с столкновением астероида с астероидом endmethod     defmethod ( _Bool , collide_with , Asteroid , Spaceship ) // борьба с столкновением астероида с космическим кораблем endmethod     defmethod ( _Bool , collide_with , Spaceship , Asteroid ) // работа с космическим кораблем, столкнувшимся с астероидом endmethod     defmethod ( _Bool , collide_with , Spaceship , Spaceship ) // борьба с столкновением космического корабля с космическим кораблем endmethod     // пример использованияint main ( void ) { OBJ a = gnew ( астероид ); OBJ s = gnew ( Космический корабль );          printf ( "<a,a> = %d \n " , столкновение_с ( a , a )); printf ( "<a,s> = %d \n " , столкновение_с ( a , s )); printf ( "<s,a> = %d \n " , столкновение_с ( s , a )); printf ( "<s,s> = %d \n " , столкновение_с ( s , s ));            смазка ( а ); жирозаменители ( а ); } 

С++

По состоянию на 2021 год C ++ изначально поддерживает только одиночную отправку, хотя добавление нескольких методов (множественная диспетчеризация) было предложено Бьярном Страуструпом (и его соавторами) в 2007 году. [20] Методы обхода этого ограничения аналогичны: используйте либо шаблон посетителя. , динамическое приведение или библиотека:

 // Пример использования сравнения типов во время выполнения через Dynamic_cast struct Thing { virtual void CollideWith ( Thing & Other ) = 0 ; };          struct Asteroid : Thing { voidcollideWith ( Thing & other ) { // Dynamic_cast к типу указателя возвращает NULL , если приведение не удалось // (dynamic_cast к ссылочному типу вызовет исключение в случае неудачи) if ( auto asteroid = Dynamic_cast < Asteroid * > ( & Other )) { // обрабатываем столкновение астероид-астероид } else if ( auto spaceship = Dynamic_cast < Spaceship *> ( & Other )) { // обрабатываем столкновение астероид-космический корабль } else { // здесь обрабатывается столкновение по умолчанию } } };                                  struct Spaceship : Thing { voidcollideWith ( Thing & other ) { if ( auto asteroid = Dynamic_cast < Asteroid *> ( & Other )) { // обрабатываем столкновение космического корабля с астероидом } else if ( auto spaceship = Dynamic_cast < Spaceship * > ( & Other )) { // обработка столкновения космических кораблей с космическими кораблями } else { // здесь обработка столкновений по умолчанию } } };                               

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

#include <cstdint> #include <typeinfo> #include <unordered_map>   класс Thing { protected : Thing ( std :: uint32_t cid ) : tid ( cid ) {} ​​const std :: uint32_t tid ; // идентификатор типа             typedef void ( Thing ::* CollisionHandler )( Thing & Other ); typedef std :: unordered_map < std :: uint64_t , CollisionHandler > CollisionHandlerMap ;        static void addHandler ( std :: uint32_t id1 , std :: uint32_t id2 , обработчик CollisionHandler ) { collisionCases . вставить ( CollisionHandlerMap :: value_type ( ключ ( id1 , id2 ), обработчик )); } static std :: uint64_t key ( std :: uint32_t id1 , std :: uint32_t id2 ) { return std :: uint64_t ( id1 ) << 32 | идентификатор2 ; }                           статический CollisionHandlerMap columnsCases ;   public : void CollideWith ( Thing & Other ) { авто обработчик = столкновениеCases . найти ( ключ ( tid , другое . tid )); if ( обработчик != столкновениеCases . end ()) { ( this ->* обработчик -> второй )( другое ); // вызов указателя на метод } else { // обработка коллизий по умолчанию } } };                      class Asteroid : public Thing { void asteroid_collision ( Thing & other ) { /*обработка столкновения астероида с астероидом*/ } void spaceship_collision ( Thing & other ) { /*обработка столкновения астероида и космического корабля*/ }                public : Asteroid () : Thing ( cid ) {} ​​static void initCases (); static const std :: uint32_t cid ; };          class Spaceship : public Thing { void asteroid_collision ( Thing & other ) { /*обработка столкновения космического корабля с астероидом*/ } void spaceship_collision ( Thing & other ) { /*обработка столкновения космического корабля с космическим кораблем*/ }               public : Космический корабль () : Thing ( cid ) {} ​​static void initCases (); static const std :: uint32_t cid ; // ID класса };           Thing :: CollisionHandlerMap Thing :: collisionCases ; const std :: uint32_t Asteroid :: cid = typeid ( Asteroid ). хэш-код (); const std :: uint32_t Космический корабль :: cid = typeid ( Космический корабль ). хэш-код ();         void Asteroid::initCases () { addHandler ( cid , cid , CollisionHandler ( & Asteroid :: asteroid_collision )); addHandler ( cid , Spaceship :: cid , CollisionHandler ( & Asteroid :: spaceship_collision )); }        void Spaceship::initCases () { addHandler ( cid , Asteroid :: cid , CollisionHandler ( & Spaceship :: asteroid_collision )); addHandler ( cid , cid , CollisionHandler ( & Spaceship :: spaceship_collision )); }        int main () { Астероид :: initCases (); Космический корабль :: initCases ();     Астероид а1 , а2 ; Космический корабль s1 , s2 ;      а1 . столкнуться с ( a2 ); а1 . столкнуться с ( s1 );  с1 . столкнуться с ( s2 ); с1 . столкнуться с ( a1 ); } 

Библиотека YOMM2 [21] обеспечивает быструю ортогональную реализацию открытых мультиметодов.

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

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

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

#include <yorel/yomm2/keywords.hpp> #include <память>  класс Вещь { общественный : виртуальный ~ Вещь () {} };      класс Астероид : общественная вещь { };     класс Космический корабль : общественная вещь { };     Register_classes ( Вещь , Космический корабль , Астероид );  Declare_method ( void , collideWith , ( virtual_ < Thing &> , virtual_ < Thing &> ));   define_method ( void , collideWith , ( Thing & left , Thing & right )) { // обработка столкновений по умолчанию }       define_method ( void , collideWith , ( Asteroid & left , Asteroid & right )) { // обрабатываем столкновение астероид-астероид }       define_method ( void , collideWith , ( Asteroid & left , Spaceship & right )) { // обрабатываем столкновение астероида и космического корабля }       define_method ( void , collideWith , ( Космический корабль & left , Астероид & right )) { // обрабатываем столкновение космического корабля с астероидом }       define_method ( void , collideWith , ( Космический корабль & влево , Космический корабль & вправо )) { // обрабатываем столкновение космического корабля с космическим кораблем }       int main () { yorel :: yomm2 :: update_methods ();    std :: unique_ptr < Thing > a1 ( std :: make_unique < Asteroid > ()), a2 ( std :: make_unique < Asteroid > ()); std : unique_ptr < Thing > s1 ( std :: make_unique < Космический корабль > ()), s2 ( std :: make_unique < Космический корабль > ()); // примечание: типы частично стерты       столкновение с ( * a1 , * a2 ); // Столкновение астероидов с астероидами CollideWith ( * a1 , * s1 ); // Столкновение астероида и космического корабля CollideWith ( * s1 , * a1 ); // Столкновение космического корабля с астероидом CollideWith ( * s1 , * s2 ); // Столкновение космического корабля с космическим кораблем            вернуть 0 ; } 

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

Д

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

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

Джава

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

UML-класс Java, одиночная диспетчеризация.svg

интерфейс  Collideable { voidcollideWith ( final Collideableother ) ;      /* Этим методам потребуются разные имена на языке без перегрузки методов. */ voidcollideWith ( последний астероид астероида ) ; void CollideWith ( последний космический корабль ); }        класс  Asteroid реализует Collideable { public void CollideWith ( Final Collideable Other ) { // Вызов CollideWith для другого объекта. другой . столкнуться с ( это ); }             public voidcollideWith ( final Asteroid asteroid ) { // Обработка столкновения астероидов с астероидами . }        public voidcollideWith ( final Spaceship spaceship ) { // Обработка столкновения астероида и космического корабля. } }       класс  Spaceship реализует Collideable { public voidcollideWith ( final Collideableother ) { // Вызов CollideWith для другого объекта. другой . столкнуться с ( это ); }             public voidcollideWith ( final Asteroid asteroid ) { // Обработка столкновения космического корабля с астероидом . }        public voidcollideWith ( final Spaceship spaceship ) { // Обработка столкновения космического корабля с космическим кораблем. } }       

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

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

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

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

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

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

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

  1. ^ Ранка, Санджай; Банерджи, Арунава; Бисвас, Канад Кишор; Дуа, Сумит; Мишра, Прабхат; Муна, Раджат (26 июля 2010 г.). Современные вычисления: Вторая международная конференция, IC3 2010, Нойда, Индия, 9–11 августа 2010 г. Материалы. Спрингер. ISBN 9783642148248.
  2. ^ abcdefghijk Мушевичи, Раду; Потанин, Алексей; Темперо, Юэн; Ноубл, Джеймс (2008). «Множественная рассылка на практике». Материалы 23-й конференции ACM SIGPLAN по языкам и приложениям объектно-ориентированных систем программирования. УПСЛА '08. Нэшвилл, Теннесси, США: ACM. стр. 563–582. дои : 10.1145/1449764.1449808. ISBN 9781605582153. S2CID  7605233.
  3. ^ abcde Безансон, Джефф; Эдельман, Алан; Карпински, Стефан; Шах, Вирал Б. (7 февраля 2017 г.). «Джулия: Свежий подход к численным вычислениям». Обзор СИАМ . 59 (1): 65–98. arXiv : 1411.1607 . дои : 10.1137/141000671. S2CID  13026838.
  4. ^ Кастанья, Джузеппе; Гелли, Джорджио и Лонго, Джузеппе (1995). «Исчисление перегруженных функций с подтипированием». Информация и вычисления . 117 (1): 115–135. дои : 10.1006/inco.1995.1033 .
  5. ^ Кастанья, Джузеппе (1996). Объектно-ориентированное программирование: единая основа. Прогресс в теоретической информатике. Биркхойзер. п. 384. ИСБН 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. doi :10.1002/j.1096-9942.1995.tb00019.x . Проверено 19 апреля 2013 г.
  8. ^ «Использование динамического типа (Руководство по программированию на C#)» . Проверено 14 мая 2020 г.
  9. ^ «Основные понятия» . Проверено 14 мая 2020 г.
  10. ^ «Динамический .NET — понимание динамического ключевого слова в C# 4» . Проверено 14 мая 2020 г.
  11. ^ Groovy — Мультиметоды
  12. ^ @arrows/multimethod Множественная диспетчеризация в JavaScript/TypeScript с настраиваемым разрешением диспетчеризации, автор Maciej Cąderek.
  13. ^ Коуди, Арик, мультиметод: диспетчеризация нескольких аргументов. , получено 28 января 2021 г.
  14. ^ multimethods.py. Архивировано 9 марта 2005 г. в Wayback Machine . Множественная диспетчеризация в Python с настраиваемым разрешением диспетчеризации, Дэвид Мерц и др.
  15. ^ «Пятиминутные мультиметоды в Python».
  16. ^ "PEAK-Правила 0.5a1.dev" . Индекс пакетов Python . Проверено 21 марта 2014 г.
  17. ^ "ПиПротоколы". Комплект корпоративных приложений Python . Проверено 26 апреля 2019 г.
  18. ^ "Рег". Прочтите документы . Проверено 26 апреля 2019 г.
  19. ^ «Объектная система C: структура, которая выводит C на уровень других языков программирования высокого уровня и выше: CObjectSystem/COS». Гитхаб . 2019-02-19.
  20. ^ «Отчет о языковой поддержке Multi-Methods и Open-Methods для C++» (PDF) . 11 марта 2007 г. Множественная диспетчеризация — выбор вызываемой функции на основе динамического типа двух или более аргументов — является решением нескольких классических проблем объектно-ориентированного программирования.
  21. ^ yomm2, Быстрые ортогональные открытые мультиметоды для C++, Жан-Луи Леруа.
  22. ^ Страуструп, Бьярн (1994). «Раздел 13.8». Проектирование и эволюция C++ . Индианаполис, Индиана, США: Эддисон Уэсли. Бибкод : 1994декабрь..книга.....С. ISBN 978-0-201-54330-8.
  23. ^ openmethods, Открытые мультиметоды для D Жана-Луи Лероя.
  24. ^ «Методы». Руководство Юлии . Джулиаланг. Архивировано из оригинала 17 июля 2016 года . Проверено 11 мая 2014 г.
  25. ^ «Мультиметоды в C# 4.0 с «Dynamic»» . Проверено 20 августа 2009 г.
  26. ^ "Язык Сесила" . Проверено 13 апреля 2008 г.
  27. ^ «Мультиметоды в Clojure» . Проверено 4 сентября 2008 г.
  28. ^ Стил, Гай Л. (1990). «28». Общий LISP: язык . Бедфорд, Массачусетс, США: Digital Press. ISBN 978-1-55558-041-4.
  29. ^ «Предыстория и цели» . Проверено 13 апреля 2008 г.
  30. ^ «Спецификация языка Fortress, версия 1.0» (PDF) . Архивировано из оригинала (PDF) 20 января 2013 г. Проверено 23 апреля 2010 г.
  31. ^ «Мультиметоды в Groovy» . Проверено 13 апреля 2008 г.
  32. ^ «Методы – LassoGuide 9.2» . Проверено 11 ноября 2014 г.
  33. ^ «Шаблон посетителей и мультиметоды» . Проверено 13 апреля 2008 г.
  34. ^ «Руководство Нима: Мультиметоды» . Проверено 3 мая 2022 г.
  35. ^ «Часто задаваемые вопросы по Perl 6» . Проверено 13 апреля 2008 г.
  36. ^ «Как работают методы S4» (PDF) . Проверено 13 апреля 2008 г.
  37. ^ «Множественная отправка в Seed7» . Проверено 23 апреля 2011 г.
  38. ^ «Системное руководство TADS 3» . Проверено 19 марта 2012 г.
  39. ^ "Множественная отправка VB.Net" . Проверено 31 марта 2020 г.
  40. ^ «Новые возможности C# 4.0 и VB.Net 10.0». 4 ноября 2010 г. Проверено 31 марта 2020 г.
  41. ^ «Заметки для экспертов по языкам программирования» . Проверено 21 августа 2016 г.
  42. ^ «Множественная отправка».

Внешние ссылки