多分派

维基百科,自由的百科全书
跳到导航 跳到搜索

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

理解分派[编辑]

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

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

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

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

数据类型[编辑]

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

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

例子[编辑]

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

具有内建多分派的语言[编辑]

Common Lisp[编辑]

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

(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

用多分派库扩展的语言[编辑]

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

JavaScript[编辑]

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

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的静态类型版本:

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
  }),
)

Python[编辑]

可以使用扩展来向Python增加多分派。例如,模块multimethod.py[5],还有模块multimethods.py[6],它为Python提供了CLOS风格的多方法而不用变更语言的底层语法或关键字。

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)

在功能上,这非常类似于CLOS例子,但是语法是常规Python的。

使用Python 2.4介入的修饰器(decorator),Guido van Rossum出品了具有简化了的语法的多方法的一个简单实现[7]

@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 ...

并且接着继续定义multimethod修饰器。

PEAK-Rules包提供语法类似上述例子的多分派[8]。它后来替代为PyProtocols[9]

Reg库也支持多分派和谓词分派[10]

模拟多分派[编辑]

C[编辑]

C语言没有动态分派,所以必须以某种形式手工实现。经常使用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);
}

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

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

// classes

defclass (Asteroid)
// data members
endclass

defclass (Spaceship)
// data members
endclass

// generics

defgeneric (_Bool, collide_with, _1, _2);

// multimethods

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

// example of use

int main(void)
{
  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);
}

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. ^ 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. ISBN 9783642148248. 
  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. ^ 4.0 4.1 4.2 @arrows/multimethod Multiple dispatch in JavaScript/TypeScript with configurable dispatch resolution by Maciej Cąderek.
  5. ^ Coady, Aric, multimethod: Multiple argument dispatching., [2021-01-28] 
  6. ^ 6.0 6.1 multimethods.py 互联网档案馆存檔,存档日期2005-03-09., Multiple dispatch in Python with configurable dispatch resolution by David Mertz, et al.
  7. ^ Five-minute Multimethods in Python. 
  8. ^ 8.0 8.1 PEAK-Rules 0.5a1.dev. Python Package Index. [21 March 2014]. 
  9. ^ PyProtocols. Python Enterprise Application Kit. [26 April 2019]. 
  10. ^ Reg. Read the docs. [26 April 2019]. 
  11. ^ 11.0 11.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. 
  12. ^ Methods. The Julia Manual. Julialang. [11 May 2014]. (原始内容存档于17 July 2016). 
  13. ^ Multimethods in C# 4.0 With 'Dynamic'. [2009-08-20]. 
  14. ^ Cecil Language. [2008-04-13]. 
  15. ^ Multimethods in Clojure. [2008-09-04]. 
  16. ^ Steele, Guy L. 28. Common LISP: The Language. Bedford, MA, U.S.A: Digital Press. 1990. ISBN 978-1-55558-041-4. 
  17. ^ Background and Goals. [2008-04-13]. 
  18. ^ The Fortress Language Specification, Version 1.0 (PDF). [2010-04-23]. (原始内容 (PDF)存档于2013-01-20). 
  19. ^ Multimethods in Groovy. [2008-04-13]. 
  20. ^ Methods – LassoGuide 9.2. [2014-11-11]. 
  21. ^ Visitor Pattern Versus Multimethods. [2008-04-13]. 
  22. ^ Nim Manual: Multi-methods. [2020-09-11]. 
  23. ^ Perl 6 FAQ. [2008-04-13]. 
  24. ^ How S4 Methods Work (PDF). [2008-04-13]. 
  25. ^ Multiple Dispatch in Seed7. [2011-04-23]. 
  26. ^ TADS 3 System Manual. [2012-03-19]. 
  27. ^ VB.Net Multiple Dispatch. [2020-03-31]. 
  28. ^ New Features in C#4.0 and VB.Net 10.0. [2020-03-31]. 
  29. ^ Notes for Programming Language Experts. [2016-08-21]. 
  30. ^ Multiple dispatch. 
  31. ^ MultiMethods.NET
  32. ^ multimethod-sharp
  33. ^ yomm2
  34. ^ multimethods
  35. ^ openmethods
  36. ^ multimethods vocabulary
  37. ^ MultiJava
  38. ^ Class::Multimethods
  39. ^ RuleDispatch
  40. ^ PyMultimethods
  41. ^ multipledispatch
  42. ^ multimethod-lib
  43. ^ The Multiple Dispatch Library
  44. ^ Multimethod Package
  45. ^ Vlx-Multimethods Package
  46. ^ TinyCLOS

外部链接[编辑]