Julia机器学习核心编程:人人可用的高性能科学计算
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.9 多重分派

函数是一个对象,它通过对一些传入参数进行一系列加工,最终可以返回一个返回值。当一个函数无法正确运行时,它就会抛出异常。如果你传入的参数不同,那么自然是想用不同的函数实现不同的功能,但这些函数的功能也许很相近,这时候Julia中的多重分派功能就可以发挥作用了。

比如,我们设计了一个函数来对两个浮点数进行求和运算,然后使用另一个函数来对两个整数进行求和运算。因为传入的参数不同,所以使用的是两个不同的函数来实现的。但从概念上讲,这两个函数做了同样的事情,就是将两个数相加。这时使用相同的函数名去调用这两个函数显得简单易用,并且更易于理解。如果使用多重分派功能,那么这两个函数不用被同时定义,只需要在使用时添加它们就可以了,而且不同参数的函数有完全不同的实现。在调用函数时,Julia会自动根据传入的参数关联合适的行为,其中每一种行为的定义都被称为方法。我们所要做的只是在调用函数时传入不同的参数就可以了,其余的工作Julia都可以十分高效地帮你完成。

在调用函数时应用对应的方法叫作分派,在平时的使用中有两种分派方法。

• 动态分派:基于运行时的类型推断。

• 多重分派:基于所有的参数,而不仅仅是接收器的名称。

Julia根据所有参数选择调用哪个方法,这种机制被称为多重分派。多重分派对数学和科学领域的代码特别有用。我们不应该将一个函数局限于一组特殊类型的参数。例如,在实现一个数学运算符时会考虑所有参数类型的操作过程,而并不是只能用于一组类型。多重分派的应用场景不局限于数学表达式,它可以在许多真实场景中使用,并且能构建出优秀的程序。

“+”符号就是Julia中定义的一个使用多重分派的函数,同时Julia的所有标准函数和运算符都使用了多重分派。对于不同参数类型的各种组合,Julia提供了许多种定义不同行为的方法。比如,你可以使用“:: type-assertion”运算符让一个函数生成一个只接受特定类型参数的方法。

01  julia> f(x::Float64, y::Float64) = x + y
02  f (generic function with 1 method)

上面这个方法只会接受两个都是Float64类型的参数,如果提供了其他类型的参数,它会弹出一个错误警告:

01  julia> f(5,10.0)
02  ERROR: MethodError: no method matching f(::Int64, ::Float64)
03  Closest candidates are:
04  f(::Float64, ::Float64) at REPL[4]:1

因为所传入的参数类型必须与函数中定义的参数类型完全相同,在定义第一个方法时会自动生成一个函数对象,之后定义新方法时,会自动添加到相应的函数对象中。在调用函数时,将自动匹配参数的数量和类型,然后执行相应的方法。下面定义另一个方法,使用两个Number类型的参数,同样也是将两个参数的值相加。这样定义后,在使用Float64类型的参数调用函数时,会自动应用第一个方法;在使用整数参数调用函数时,会自动应用第二个方法。从直观上讲,我们使用的是同一个函数名。

01  julia> f(x::Number, y::Number) = x + y
02  f (generic function with 2 methods)
03  julia> f(100,200)
04  300

在Julia中,所有值都是抽象类型Any的实例。如果没有使用“::”指明参数的类型,那么该参数的类型就是Any,它没有对传入的值进行限制。如果你定义了一个函数,并且对这个函数的用途很明确,那么在该函数只含有一个方法的情况下,可以不使用“::”来约束参数类型。关于多重分派先介绍到这里,我们会在后续的章节中进行详细研究。