Из Википедии, бесплатной энциклопедии
  (Перенаправлено из Multimethods )
Перейти к навигации Перейти к поиску

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

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

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

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

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

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

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

Типы данных [ править ]

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

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

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

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

Чтобы оценить, насколько часто на практике используется множественная отправка, 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 , где множественная отправка была центральной концепцией дизайна с момента зарождения языка: собирая ту же статистику, что и Muschevici, по среднему количеству методов на общую функцию, было обнаружено, что стандартная библиотека Julia использует более чем в два раза превышает объем перегрузки, чем на других языках, проанализированных Muschevici, и более чем в 10 раз в случае бинарных операторов . [3]

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

Теория [ править ]

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

Примеры [ править ]

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

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

C # [ править ]

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

class  Program {  static  void  Main ()  {  Консоль . WriteLine ( Коллайдер . Столкновение ( новый  астероид ( 101 ),  новый  космический корабль ( 300 )));  Консоль . WriteLine ( Коллайдер . Столкновение ( новый  астероид ( 10 ),  новый  космический корабль ( 10 )));  Консоль . WriteLine ( Collider .Столкновение ( новый  космический корабль ( 101 ),  новый  космический корабль ( 10 )));  } }статический  класс  Collider {  общедоступная  статическая  строка  Collide ( SpaceObject  x ,  SpaceObject  y )  =>  (( x . Size  >  100 )  &&  ( y . Size  >  100 ))  ?  "Большой бум!"  :  CollideWith ( x  как  динамический ,  y  как  динамический );  частная  статическая  строка  CollideWith ( Asteroid  x,  Астероид  y )  =>  "а / а" ;  частная  статическая  строка  CollideWith ( Asteroid  x ,  Spaceship  y )  =>  "a / s" ;  частная  статическая  строка  CollideWith ( Spaceship  x ,  Asteroid  y )  =>  "s / a" ;  частная  статическая  строка  CollideWith ( Spaceship  x ,  Spaceship  y )  =>  "s / s" ; }абстрактный  класс  SpaceObject {  общедоступный  SpaceObject ( int  size )  =>  Size  =  size ; общедоступный  int  Size  {  получить ;  } }class  Asteroid  :  SpaceObject {  public  Asteroid ( int  size )  :  base ( size )  {  } }class  Spaceship  :  SpaceObject {  public  Spaceship ( int  size )  :  base ( size )  {  } }

Выход:

большая стрелав видеSS

Groovy [ править ]

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

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

Common Lisp [ править ]

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

( defmethod  collide-with  (( x  asteroid )  ( y  asteroid ))  ;; иметь дело с астероидом, поражающим астероид  ) ( defmethod  collide-with  (( x  asteroid )  ( y  космический корабль ))  ;; иметь дело с астероидом, поражающим космический корабль  ) ( defmethod  collide- with  (( x  космический корабль )  ( y  астероид ))  ;; иметь дело с космическим кораблем, поражающим астероид  ) ( defmethod  collide-with  (( x космический корабль )  ( у  космический корабль ))  ;; иметь дело с космическим кораблем, поражающим космический корабль  )

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

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

Юлия [ править ]

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

collide_with ( x :: Asteroid ,  y :: Asteroid )  =  ...  # обработать астероид, поражающий астероид collide_with ( x :: Asteroid ,  y :: Spaceship )  =  ...  # обработать астероид, поражающий космический корабль collide_with ( x :: Spaceship ,  y :: Asteroid )  =  ...  # справиться с столкновением космического корабля с астероидом collide_with ( x :: Spaceship , y :: Spaceship )  =  ...  # разобраться с космическим кораблем, поражающим космический корабль

Оболочка нового поколения [ править ]

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

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

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

{ type SpaceObject type Asteroid ( SpaceObject ) type Spaceship ( SpaceObject ) }F  init ( o : SpaceObject ,  размер : Int )  o . size  =  размерF  collide ( x : астероид ,  y : астероид )  "a / a" F  столкновение ( x : астероид ,  y : космический корабль )  "a / s" F  столкновение ( x : космический корабль ,  y : астероид )  "s / a" F  столкновение ( x : космический корабль ,  y : космический корабль )  "s / s"F  collide ( x : SpaceObject ,  y : SpaceObject )  { охранник  x . размер  >  100 караул  у . size  >  100 "big-boom" }эхо ( столкновение ( Астероид ( 101 ),  Космический корабль ( 300 ))) эхо ( столкновение ( Астероид ( 10 ),  Космический корабль ( 10 )))

Выход:

большая стрелав виде

Раку [ править ]

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

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

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

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

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

JavaScript [ править ]

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

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

импортировать  {  мульти ,  метод  }  из  '@ arrow / multimethod'class  Asteroid  {} class  Spaceship  {}const  collideWith  =  multi (  method ([ Asteroid ,  Asteroid ],  ( x ,  y )  =>  {  // дело с астероидом, поражающим астероид  }),  method ([ Asteroid ,  Spaceship ],  ( x ,  y )  =>  {  // дело с астероидом, поражающим космический корабль  }),  method ([ Spaceship ,  Asteroid ],  ( x ,  y )  =>  { // обработка столкновения космического корабля с астероидом  }),  method ([ Spaceship ,  Spaceship ],  ( x ,  y )  =>  {  // решение проблемы столкновения космического корабля с космическим кораблем  }), )

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

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

Python [ править ]

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

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

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

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

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

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

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

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

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

C [ править ]

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

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

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

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

C ++ [ править ]

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

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

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

#include  <cstdint>#include  <typeinfo>#include  <неупорядоченная_карта>class  Thing  {  protected :  Thing ( std :: uint32_t  cid )  :  tid ( cid )  {}  const  std :: uint32_t  tid ;  // тип id typedef  void  ( Вещь :: * Обработчик столкновений ) ( Вещь и  другие );  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 ), обработчик )); } статический ключ std :: uint64_t ( std :: uint32_t id1 , std :: uint32_t           id2 )  {  return  std :: uint64_t ( id1 )  <<  32  |  id2 ;  } static  CollisionHandlerMap  collisionCases ; public :  void  collideWith ( Thing &  other )  {  auto  handler  =  collisionCases . find ( key ( tid ,  other . tid ));  if  ( обработчик  ! =  collisionCases . end ())  {  ( это -> * обработчик -> второй ) ( другое );  // вызов указателя на метод  }  else  {  // обработка столкновений по умолчанию  } } };class  Asteroid :  public  Thing  {  void  asteroid_collision ( Thing &  other )  {  / * обрабатывать столкновение астероид-астероид * /  }  void  spacehip_collision ( Thing &  other )  {  / * обрабатывать столкновение астероид-космический корабль * / } public :  Asteroid () :  Thing ( cid )  {}  static  void  initCases ();  статическая  константа  std :: uint32_t  cid ; };class  Spaceship :  public  Thing  {  void  asteroid_collision ( Thing &  other )  {  / * обрабатывать столкновение космического корабля с астероидом * / }  void  spacehip_collision ( Thing &  other )  {  / * обрабатывать столкновение космического корабля-космического корабля * / } public :  Spaceship () :  Thing ( cid )  {}  static  void  initCases ();  статическая  константа  std :: uint32_t  cid ;  // идентификатор класса };Thing :: CollisionHandlerMap  Thing :: collisionCases ; const  std :: uint32_t  Астероид :: cid  =  typeid ( Астероид ). hash_code (); const  std :: uint32_t  Космический корабль :: cid  =  typeid ( Космический корабль ). hash_code ();void  Asteroid :: initCases ()  {  addHandler ( cid ,  cid ,  CollisionHandler ( & Asteroid :: asteroid_collision ));  addHandler ( cid ,  Spaceship :: cid ,  CollisionHandler ( & Asteroid :: spacehip_collision )); }void  Spaceship :: initCases ()  {  addHandler ( cid ,  Asteroid :: cid ,  CollisionHandler ( & Spaceship :: asteroid_collision ));  addHandler ( cid ,  cid ,  CollisionHandler ( & Spaceship :: spacehip_collision )); }int  main ()  {  Астероид :: initCases ();  Космический корабль :: initCases (); Астероид  а1 ,  а2 ;  Космический корабль  s1 ,  s2 ; а1 . collideWith ( a2 );  а1 . collideWith ( s1 ); s1 . collideWith ( s2 );  s1 . collideWith ( a1 ); }

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

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

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

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

# включить  <yorel / yomm2 / cute.hpp>используя  yorel :: yomm2 :: virtual_ ;class  Thing  {  public :  virtual  ~ Thing ()  {}  // ... };class  Asteroid  :  public  Thing  {  // ... };class  Spaceship  :  public  Thing  {  // ... };register_class ( Вещь ); register_class ( Космический корабль ,  Вещь ); register_class ( Астероид ,  Вещь );declare_method ( void ,  collideWith ,  ( виртуальная_ < вещь &> ,  виртуальная_ < вещь &> ));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 ,  ( Spaceship &  left ,  Asteroid &  right ))  {  // обрабатываем столкновение Spaceship-Asteroid }define_method ( void ,  collideWith ,  ( Spaceship &  left ,  Spaceship &  right ))  {  // обрабатываем столкновение космического корабля-космического корабля }int  main ()  {  yorel :: yomm2 :: update_methods (); Астероид  а1 ,  а2 ;  Космический корабль  s1 ,  s2 ; collideWith ( a1 ,  a2 );  collideWith ( a1 ,  s1 ); collideWith ( s1 ,  s2 );  collideWith ( s1 ,  a1 ); }

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

D [ править ]

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

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

Java [ править ]

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

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

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

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

Первичная парадигма [ править ]

  • Юлия [26]

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

  • C # 4.0 [27]
  • Сесил [28]
  • Clojure [29]
  • Common Lisp (через объектную систему Common Lisp ) [30]
  • Дилан [31]
  • Эликсир [32]
  • Крепость [33]
  • Отличный [34]
  • Лассо [35] [36]
  • Nim , до v0.19.x (несколько методов устарели в v0.20) [37]
  • Раку [38]
  • R [39]
  • Seed7 [40]
  • TADS [41]
  • VB.Net [42] через позднее связывание, также через .Net DLR [43]
  • Wolfram Language [44] через символьное сопоставление с образцом
  • Xtend [45]

Через расширения [ править ]

  • Любой язык .NET (через библиотеку MultiMethods.NET )
  • C (через библиотеку C Object System )
  • C # (через библиотеку multimethod-sharp )
  • C ++ (через библиотеку yomm2 и мультиметоды )
  • D (через библиотеку openmethods )
  • Фактор (через стандартный словарь мультиметодов )
  • Java (с использованием расширения MultiJava )
  • JavaScript (через пакет @ arrow / multimethod )
  • Perl (через модуль Class :: Multimethods )
  • Python (через PEAK-Rules , RuleDispatch , gnosis.magic.multimethods , PyMultimethods или множественную рассылку )
  • Ракетка (через multimethod-lib )
  • Рубин (через библиотеку Множественная диспетчерская Библиотека и Многометодная пакет и VLX-Мультиметоды пакет )
  • Схема (например, через TinyCLOS )
  • TypeScript (через пакет @ arrow / multimethod )

См. Также [ править ]

  • Предикатная отправка

Ссылки [ править ]

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

Внешние ссылки [ править ]

  • Страуструп, Бьярн; Солодкий, Юрий; Пиркельбауэр, Питер (2007). Открытые мульти-методы для C ++ (PDF) . 6-я Международная конференция ACM по генеративному программированию и компонентной инженерии.
  • «Динамическая множественная рассылка» . docs.racket-lang.org . Проверено 12 марта 2018 .