For faster navigation, this Iframe is preloading the Wikiwand page for 多分派.

多分派

维基百科,自由的百科全书

多分派或译多重派发(multiple dispatch)或多方法(multimethod),是某些编程语言的一个特性,其中的函数或者方法,可以在运行时间(动态的)基于它的实际参数的类型,或在更一般的情况下于此之外的其他特性,来动态分派[1]。这是对单分派多态的推广, 那里的函数或方法调用,基于在其上调用方法的对象的派生类型,而动态分派。多分派使用一个或多个实际参数的组合特征,路由动态分派至实现函数或方法。

理解分派

软件工程师通常把代码写进代码块中,代码块通常称作过程、函数、方法。代码通过被调用来执行,调用时将控制权传入函数中,当函数执行完成后将控制权返回给调用者。

函数名通常用来描述函数的目的。有时会将多个函数起同样的名称。比如同名函数在逻辑上处理相同的任务,但是操作在不同类型的输入值上。在这种情况下,无法仅仅通过函数名,来判断目标代码块。那么,函数的实际参数的个数和类型,也就被用来判断。

通常,单分派面向对象语言,在调用一个方法时,方法参数中一个参数会被特殊对待,并用来决定哪一个方法(如果有多个同名方法)会被调用。在许多语言中,这个特殊的参数是在语法上指明的,许多编程语言在调用方法时,把特殊参数放在小圆点(.)之前。例如 special.method(other, arguments, here),这样 lion.sound() 将会发出狮吼,同时 sparrow.sound() 只会吱吱地叫。一般来说,对于面向对象的编程语言,这个小圆点之前的参数(上例中的lionsparrow)被称为接收者[2]

相反,在实现了多分派的语言中,被调用的函数,即是那些参数个数一样多,并且类型也匹配的调用。在调用中并没有特殊参数,来决定那个方法被调用。也就是说,所有参数的运行时类型都参与分派。CLOS是早期和著名的多分派语言。

数据类型

对于编译时间可以区分数据类型的编程语言,在交替英语Alternation (formal language theory)(alternative)函数中进行选择,可以发生在编译时间,创建交替函数用于编译时间选择的活动,通常被叫做函数重载

在有些编程语言中,这种数据类型的识别,可以被延后至运行时间(后期绑定英语Late binding)。交替函数的选择发生在运行时间,并依据动态确定的函数实际参数的类型。以这种方式选择交替实现的函数,通常被称为多方法。

例子

可以通过例子更加清晰的区分多分派和单一分派。假想一个游戏,它有两种(用户可见的)物体:飞船和小行星。当两个物体要相撞的时候,程序需要依据什么物体要相撞而做不同的事情。

具有内建多分派的语言

Common Lisp

在具有多分派的Common Lisp语言中,可以在Common Lisp对象系统中如下这样实现:

(defgeneric collide (x y))
(defclass asteroid () ())
(defclass spaceship () ())
(defmethod collide-with ((x asteroid) (y asteroid))
  ;; deal with asteroid hitting asteroid
  )
(defmethod collide-with ((x asteroid) (y spaceship))
  ;; deal with asteroid hitting spaceship
  )
(defmethod collide-with ((x spaceship) (y asteroid))
  ;; deal with spaceship hitting asteroid
  )
(defmethod collide-with ((x spaceship) (y spaceship))
  ;; deal with spaceship hitting spaceship
  )

并且对其他方法也是类似的。没有使用显式测试和“动态转换”。

由于多分派的存在,方法要定义在类中并包含在对象中的传统想法,变得不再吸引人了,上述每个collide-with方法,都附属于两个不同的类而非一个类。因此方法调用的特殊语法,一般会消失,从而方法调用看起来完全就像正常的函数调用,并且方法被组织入泛化函数而非类中。

Julia

Julia有内建的多分派,并且它是语言设计的中心[3]。Julia版本的例子如下:

collide_with(x::Asteroid, y::Asteroid) = ... # deal with asteroid hitting asteroid
collide_with(x::Asteroid, y::Spaceship) = ... # deal with asteroid hitting spaceship
collide_with(x::Spaceship, y::Asteroid) = ... # deal with spaceship hitting asteroid
collide_with(x::Spaceship, y::Spaceship) = ... # deal with spaceship hitting spaceship

C#

C#在版本4(2010年4月),使用关键字dynamic,介入了对动态多方法的支持[4]。下面的例子展示多方法协同于在版本8(2019年9月)中介入的switch表达式[5]。像很多其他静态类型的语言语言一样,C#还支持静态方法重载[6],Microsoft预期开发者在多数场景下会选用静态类型超过动态类型[7]dynamic关键字支持COM对象和动态类型的.NET语言的互操作。

class Program
{
    static void Main()
    {
        Console.WriteLine(Collider.Collide(new Asteroid(101),  new Spaceship(300)));
        Console.WriteLine(Collider.Collide(new Asteroid(10),   new Spaceship(10)));
        Console.WriteLine(Collider.Collide(new Spaceship(101), new Spaceship(10)));
    }
}

static class Collider
{
    public static string Collide(SpaceObject x, SpaceObject y) =>
        ((x.Size > 100) && (y.Size > 100)) ?
            "Big boom!" : CollideWith(x as dynamic, y as dynamic);
    private static string CollideWith(Asteroid x, Asteroid y) => "a/a";
    private static string CollideWith(Asteroid x, Spaceship y) => "a/s";
    private static string CollideWith(Spaceship x, Asteroid y) => "s/a";
    private static string CollideWith(Spaceship x, Spaceship y) => "s/s";
}

abstract class SpaceObject
{
    public SpaceObject(int size) => Size = size;

    public int Size { get; }
}

class Asteroid : SpaceObject
{
    public Asteroid(int size) : base(size) { }
}

class Spaceship : SpaceObject
{
    public Spaceship(int size) : base(size) { }
}

输出:

big-boom
a/s
s/s

Groovy

Groovy是通用的Java兼容/互用的JVM语言,它对立于Java,使用后期绑定/多分派[8]

/*
    Groovy implementation of C# example above
    Late binding works the same when using non-static methods or compiling class/methods statically
    (@CompileStatic annotation)
*/
class Program {
    static void main(String[] args) {
        println Collider.collide(new Asteroid(101), new Spaceship(300))
        println Collider.collide(new Asteroid(10), new Spaceship(10))
        println Collider.collide(new Spaceship(101), new Spaceship(10))
    }
}

class Collider {
    static String collide(SpaceObject x, SpaceObject y) {
        (x.size > 100 && y.size > 100) ? "big-boom" : collideWith(x, y)  // Dynamic dispatch to collideWith method
    }

    private static String collideWith(Asteroid x, Asteroid y) { "a/a" }
    private static String collideWith(Asteroid x, Spaceship y) { "a/s" }
    private static String collideWith(Spaceship x, Asteroid y) { "s/a" }
    private static String collideWith(Spaceship x, Spaceship y) { "s/s"}
}

class SpaceObject {
    int size
    SpaceObject(int size) { this.size = size }
}

@InheritConstructors class Asteroid extends SpaceObject {}
@InheritConstructors class Spaceship extends SpaceObject {}

用多分派库扩展的语言

在不于语言定义或语法层次支持多分派的语言中,可能经常使用扩展来增加多分派。

JavaScript

JavaScriptTypeScript不在语言语法层次上支持多方法,但可以通过库来增加多分派。例如,使用multimethod包[9],它提供了多分派、泛化函数的实现。JavaScript的动态类型版本:

import { multi, method } from '@arrows/multimethod'

class Asteroid {}
class Spaceship {}

const collideWith = multi(
  method([Asteroid, Asteroid], (x, y) => {
    // deal with asteroid hitting asteroid
  }),
  method([Asteroid, Spaceship], (x, y) => {
    // deal with asteroid hitting spaceship
  }),
  method([Spaceship, Asteroid], (x, y) => {
    // deal with spaceship hitting asteroid
  }),
  method([Spaceship, Spaceship], (x, y) => {
    // deal with spaceship hitting spaceship
  }),
)

TypeScript有对应的静态类型版本。[a]

Python

可以使用扩展来向Python增加多分派。例如,最早的模块multimethods.py[10],它为Python提供了CLOS风格的多方法而不用变更语言的底层语法或关键字。在功能上,这非常类似于CLOS例子,但是语法是常规Python的。[b] Guido van Rossum使用Python 2.4介入的修饰器(decorator),出品了多方法的具有简化了的语法的一个简单实现,他为此定义了multimethod修饰器[11][c] multipledispatch[12]采用的形式与之一致。

模块multimethod[13],采用了修饰器和Python 3.5介入的类型提示实现多方法:

from multimethod import multimethod

class Asteroid(): pass

class Spaceship(): pass

@multimethod
def collide_with(x: Asteroid, y: Asteroid):
    '''deal with asteroid hitting asteroid'''
    print("asteroid hitting asteroid")

@multimethod
def collide_with(x: Asteroid, y: Spaceship):
    '''deal with asteroid hitting spaceship'''
    print("asteroid hitting spaceship")

@multimethod
def collide_with(x: Spaceship, y: Asteroid):
    '''deal with spaceship hitting asteroid'''
    print("spaceship hitting asteroid")

@multimethod
def collide_with(x: Spaceship, y: Spaceship):
    '''deal with spaceship hitting spaceship'''
    print("spaceship hitting spaceship")
>>> a = Asteroid()
>>> b = Spaceship()
>>> collide_with(a, b)
asteroid hitting spaceship

此外,还有PEAK-Rules包提供语法类似上述例子的多分派[14],它后来被替代为PyProtocols[15]。Reg库也支持多分派和谓词分派[16]

C

C语言使用C Object System库[17],可以支持类似于CLOS的动态分派。它是完全可扩展的并且方法不需要任何的手工处理。动态消息(方法)通过COS分派器来分派,它比Objective-C更快。下面是使用COS的例子:

#include <stdio.h>
#include <cos/Object.h>
#include <cos/gen/object.h>

/* 类 */
defclass (Asteroid)
/* 数据成员 */
endclass

defclass (Spaceship)
/* 数据成员 */
endclass

/* 泛化函数 */
defgeneric (_Bool, collide_with, _1, _2);

/* 多方法 */
defmethod (_Bool, collide_with, Asteroid, Asteroid)
 /* deal with asteroid hitting asteroid */
endmethod

defmethod (_Bool, collide_with, Asteroid, Spaceship)
 /* deal with asteroid hitting spaceship */
endmethod

defmethod (_Bool, collide_with, Spaceship, Asteroid)
 /* deal with spaceship hitting asteroid */
endmethod

defmethod (_Bool, collide_with, Spaceship, Spaceship)
 /* deal with spaceship hitting spaceship */
endmethod

/* 用例 */
int main(int argc, char *argv[])
{
  OBJ a = gnew(Asteroid);
  OBJ s = gnew(Spaceship);

  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(a);
  grelease(s);
}

编程语言支持

主范型

支持通用的多方法

通过扩展

模拟多分派

C

C语言没有动态分派,也可以不使用C Object System库,而以某种形式手工实现。动态分派经常使用enum来标识一个对象的子类型,然后可通过在函数指针分支表英语branch table中查找这个值来完成。C语言模拟多方法的简单例子:

typedef void (*CollisionCase)(void);

void collision_AA(void) { /* handle Asteroid-Asteroid collision  */ };
void collision_AS(void) { /* handle Asteroid-Spaceship collision */ };
void collision_SA(void) { /* handle Spaceship-Asteroid collision */ };
void collision_SS(void) { /* handle Spaceship-Spaceship collision*/ };

typedef enum {
    THING_ASTEROID = 0,
    THING_SPACESHIP,
    THING_COUNT /* not a type of thing itself, instead used to find number of things */
} Thing;

CollisionCase collisionCases[THING_COUNT][THING_COUNT] = {
    {&collision_AA, &collision_AS},
    {&collision_SA, &collision_SS}
};

void collide(Thing a, Thing b) {
    (*collisionCases[a][b])();
}

int main(void) {
    collide(THING_SPACESHIP, THING_ASTEROID);
}

Java

在只有单一分派的语言比如Java中,多分派可以用多层单一分派来模拟:

interface Collideable {
    void collideWith(final Collideable other);

    /* These methods would need different names in a language without method overloading. */
    void collideWith(final Asteroid asteroid);
    void collideWith(final Spaceship spaceship);
}

class Asteroid implements Collideable {
    public void collideWith(final Collideable other) {
        // Call collideWith on the other object.
        other.collideWith(this);
   }

    public void collideWith(final Asteroid asteroid) {
        // Handle Asteroid-Asteroid collision.
    }

    public void collideWith(final Spaceship spaceship) {
        // Handle Asteroid-Spaceship collision.
    }
}

class Spaceship implements Collideable {
    public void collideWith(final Collideable other) {
        // Call collideWith on the other object.
        other.collideWith(this);
    }

    public void collideWith(final Asteroid asteroid) {
        // Handle Spaceship-Asteroid collision.
    }

    public void collideWith(final Spaceship spaceship) {
        // Handle Spaceship-Spaceship collision.
    }
}

运行时间instanceof检查可以在一个或两个层次上使用。

代码示例

  1. ^ TypeScript的多方法示例:
    import { multi, method, Multi } from '@arrows/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) => {
        // deal with asteroid hitting asteroid
      }),
      method([Asteroid, Spaceship], (x, y) => {
        // deal with asteroid hitting spaceship
      }),
      method([Spaceship, Asteroid], (x, y) => {
        // deal with spaceship hitting asteroid
      }),
      method([Spaceship, Spaceship], (x, y) => {
        // deal with spaceship hitting spaceship
      }),
    )
    
  2. ^ Python的multimethods.py示例:
    from multimethods import Dispatch
    from game_objects import Asteroid, Spaceship
    from game_behaviors import as_func, ss_func, sa_func
    collide = Dispatch()
    collide.add_rule((Asteroid, Spaceship), as_func)
    collide.add_rule((Spaceship, Spaceship), ss_func)
    collide.add_rule((Spaceship, Asteroid), sa_func)
    def aa_func(a, b):
        """Behavior when asteroid hits asteroid."""
        # ...define new behavior...
    collide.add_rule((Asteroid, Asteroid), aa_func)
    
    # ...later...
    collide(thing1, thing2)
    
  3. ^ Python的van Rossum最初的多方法实现:
    @multimethod(Asteroid, Asteroid)
    def collide(a, b):
        """Behavior when asteroid hits a asteroid."""
        # ...define new behavior...
    @multimethod(Asteroid, Spaceship)
    def collide(a, b):
        """Behavior when asteroid hits a spaceship."""
        # ...define new behavior...
    # ... define other multimethod rules ...
    

引用

  1. ^ Ranka, Sanjay; Banerjee, Arunava; Biswas, Kanad Kishore; Dua, Sumeet; Mishra, Prabhat; Moona, Rajat. Contemporary Computing: Second International Conference, IC3 2010, Noida, India, August 9–11, 2010. Proceedings. Springer. 2010-07-26 [2021-03-23]. ISBN 9783642148248. (原始内容存档于2021-04-27). 
  2. ^ Igor Wojda. Programmer dictionary: Receiver. Kt.Academy. 2018-02-08 [2020-02-27] (英语). 
  3. ^ Bezanson, Jeff; Edelman, Alan; Karpinski, Stefan; Shah, Viral B. Julia: A fresh approach to numerical computing. SIAM Review. 7 February 2017, 59 (1): 65–98. arXiv:1411.1607可免费查阅. doi:10.1137/141000671. 
  4. ^ Using type dynamic (C# Programming Guide). [2020-05-14]. (原始内容存档于2021-05-26). 
  5. ^ switch expression (C# reference). [2020-05-14]. (原始内容存档于2021-06-28). 
  6. ^ Basic concepts. [2020-05-14]. (原始内容存档于2021-04-16). 
  7. ^ Dynamic .NET - Understanding the Dynamic Keyword in C# 4. [2020-05-14]. (原始内容存档于2021-05-24). 
  8. ^ Groovy - Multi-methods. [2021-04-15]. (原始内容存档于2021-04-16). 
  9. ^ 9.0 9.1 9.2 @arrows/multimethod页面存档备份,存于互联网档案馆) Multiple dispatch in JavaScript/TypeScript with configurable dispatch resolution by Maciej Cąderek.
  10. ^ multimethods.py页面存档备份,存于互联网档案馆), Multiple dispatch in Python with configurable dispatch resolution by David Mertz, et al.
  11. ^ Five-minute Multimethods in Python. [2014-07-13]. (原始内容存档于2021-05-29). 
  12. ^ 12.0 12.1 multipledispatch. [2021-04-15]. (原始内容存档于2020-11-11). 
  13. ^ 13.0 13.1 Coady, Aric, multimethod: Multiple argument dispatching., [2021-01-28], (原始内容存档于2020-12-31) 
  14. ^ PEAK-Rules 0.5a1.dev. Python Package Index. [21 March 2014]. (原始内容存档于2017-03-14). 
  15. ^ PyProtocols. Python Enterprise Application Kit. [26 April 2019]. (原始内容存档于2021-05-05). 
  16. ^ 16.0 16.1 Reg. Read the docs. [26 April 2019]. (原始内容存档于2021-03-05). 
  17. ^ 17.0 17.1 C Object System: A framework that brings C to the level of other high level programming languages and beyond: CObjectSystem/COS. 2019-02-19 [2021-03-30]. (原始内容存档于2021-05-01). 
  18. ^ Methods. The Julia Manual. Julialang. [11 May 2014]. (原始内容存档于2016-07-17). 
  19. ^ Multimethods in C# 4.0 With 'Dynamic'. [2009-08-20]. (原始内容存档于2009-08-25). 
  20. ^ Cecil Language. [2008-04-13]. (原始内容存档于2016-09-01). 
  21. ^ Multimethods in Clojure. [2008-09-04]. (原始内容存档于2015-09-20). 
  22. ^ Steele, Guy L. 28. Common LISP: The Language. Bedford, MA, U.S.A: Digital Press. 1990 [2021-03-30]. ISBN 978-1-55558-041-4. (原始内容存档于2017-12-17). 
  23. ^ Background and Goals. [2008-04-13]. (原始内容存档于2020-04-04). 
  24. ^ The Fortress Language Specification, Version 1.0 (PDF). [2010-04-23]. (原始内容 (PDF)存档于2013-01-20). 
  25. ^ Multimethods in Groovy. [2008-04-13]. (原始内容存档于2011-08-12). 
  26. ^ Methods – LassoGuide 9.2. [2014-11-11]. (原始内容存档于2021-06-13). 
  27. ^ Visitor Pattern Versus Multimethods. [2008-04-13]. (原始内容存档于2021-02-05). 
  28. ^ Nim Manual: Multi-methods. [2020-09-11]. (原始内容存档于2021-06-15). 
  29. ^ Perl 6 FAQ. [2008-04-13]. (原始内容存档于2012-03-13). 
  30. ^ How S4 Methods Work (PDF). [2008-04-13]. (原始内容存档 (PDF)于2021-05-10). 
  31. ^ Multiple Dispatch in Seed7. [2011-04-23]. (原始内容存档于2021-01-29). 
  32. ^ TADS 3 System Manual. [2012-03-19]. (原始内容存档于2017-02-14). 
  33. ^ VB.Net Multiple Dispatch. [2020-03-31]. (原始内容存档于2021-04-11). 
  34. ^ New Features in C#4.0 and VB.Net 10.0. [2020-03-31]. (原始内容存档于2021-02-01). 
  35. ^ Notes for Programming Language Experts. [2016-08-21]. (原始内容存档于2021-06-26). 
  36. ^ Multiple dispatch. [2021-03-30]. (原始内容存档于2021-05-08). 
  37. ^ MultiMethods.NET. [2014-07-13]. (原始内容存档于2010-02-26). 
  38. ^ multimethod-sharp. [2021-03-30]. (原始内容存档于2016-01-23). 
  39. ^ yomm2. [2021-03-30]. (原始内容存档于2020-11-12). 
  40. ^ multimethods. [2021-03-30]. (原始内容存档于2020-11-22). 
  41. ^ openmethods
  42. ^ multimethods vocabulary. [2014-07-13]. (原始内容存档于2021-04-29). 
  43. ^ MultiJava. [2014-07-13]. (原始内容存档于2021-04-11). 
  44. ^ Class::Multimethods. [2014-07-13]. (原始内容存档于2013-10-21). 
  45. ^ multimethod-lib. [2021-03-30]. (原始内容存档于2021-04-29). 
  46. ^ The Multiple Dispatch Library. [2021-03-30]. (原始内容存档于2021-04-30). 
  47. ^ Multimethod Package. [2021-03-30]. (原始内容存档于2021-04-29). 
  48. ^ Vlx-Multimethods Package. [2021-03-30]. (原始内容存档于2021-04-27). 
  49. ^ TinyCLOS. [2014-07-13]. (原始内容存档于2008-12-11). 

外部链接

  • Stroustrup, Bjarne; Solodkyy, Yuriy; Pirkelbauer, Peter. Open Multi-Methods for C++ (PDF). ACM 6th International Conference on Generative Programming and Component Engineering. 2007 [2014-07-13]. (原始内容存档 (PDF)于2021-04-29). 
  • Dynamic multiple dispatch. docs.racket-lang.org. [2018-03-12]. (原始内容存档于2021-04-29). 
{{bottomLinkPreText}} {{bottomLinkText}}
多分派
Listen to this article

This browser is not supported by Wikiwand :(
Wikiwand requires a browser with modern capabilities in order to provide you with the best reading experience.
Please download and use one of the following browsers:

This article was just edited, click to reload
This article has been deleted on Wikipedia (Why?)

Back to homepage

Please click Add in the dialog above
Please click Allow in the top-left corner,
then click Install Now in the dialog
Please click Open in the download dialog,
then click Install
Please click the "Downloads" icon in the Safari toolbar, open the first download in the list,
then click Install
{{::$root.activation.text}}

Install Wikiwand

Install on Chrome Install on Firefox
Don't forget to rate us

Tell your friends about Wikiwand!

Gmail Facebook Twitter Link

Enjoying Wikiwand?

Tell your friends and spread the love:
Share on Gmail Share on Facebook Share on Twitter Share on Buffer

Our magic isn't perfect

You can help our automatic cover photo selection by reporting an unsuitable photo.

This photo is visually disturbing This photo is not a good choice

Thank you for helping!


Your input will affect cover photo selection, along with input from other users.