调度类型
在 Julia 上,你可以为每个函数定义多个方法。假设我们定义了三个相同函数的方法:
foo(x) = 1
foo(x::Number) = 2
foo(x::Int) = 3
在决定使用什么方法(称为调度 )时,Julia 选择与参数类型匹配的更具体的方法:
julia> foo('one')
1
julia> foo(1.0)
2
julia> foo(1)
3
这有利于多态性 。例如,我们可以通过定义两个名为 Nil
和 Cons
的不可变类型轻松创建链表 。这些名称传统上分别用于描述空列表和非空列表。
abstract LinkedList
immutable Nil <: LinkedList end
immutable Cons <: LinkedList
first
rest::LinkedList
end
我们将通过 Nil()
和 Cons(first, rest)
的任何其他列表来表示空列表,其中 first
是链表的第一个元素,rest
是由所有剩余元素组成的链表。例如,列表 [1, 2, 3]
将表示为
julia> Cons(1, Cons(2, Cons(3, Nil())))
Cons(1,Cons(2,Cons(3,Nil())))
清单是空的吗?
假设我们想要扩展标准库的 isempty
函数,该函数适用于各种不同的集合:
julia> methods(isempty)
# 29 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
...
我们可以简单地使用函数调度语法,并定义 isempty
的另外两种方法。由于此功能来自 Base
模块,因此我们必须将其限定为 Base.isempty
才能扩展它。
Base.isempty(::Nil) = true
Base.isempty(::Cons) = false
在这里,我们根本不需要参数值来确定列表是否为空。仅仅类型就足以计算该信息。如果我们不需要使用它们的值,Julia 允许我们省略参数的名称,只保留它们的类型注释。
我们可以测试我们的 isempty
方法是否有效:
julia> using Base.Test
julia> @test isempty(Nil())
Test Passed
Expression: isempty(Nil())
julia> @test !isempty(Cons(1, Cons(2, Cons(3, Nil()))))
Test Passed
Expression: !(isempty(Cons(1,Cons(2,Cons(3,Nil())))))
事实上,isempty
的方法数量增加了 14:
julia> methods(isempty)
# 31 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
显然,确定链表是否为空是一个简单的例子。但它导致更有趣的事情:
这份清单有多长?
标准库中的 length
函数为我们提供了集合或某些可迭代的长度。有很多方法可以为链表实现 length
。特别是,在 Julia 中使用 while
循环可能是最快且最节省内存的。但是要避免过早优化 ,所以让我们假设我们的链表无需高效。编写 length
函数最简单的方法是什么?
Base.length(::Nil) = 0
Base.length(xs::Cons) = 1 + length(xs.rest)
第一个定义很简单:空列表的长度为 0
。第二个定义也很容易理解:为了计算列表的长度,我们计算第一个元素,然后计算列表其余部分的长度。我们可以像测试 isempty
一样测试这种方法:
julia> @test length(Nil()) == 0
Test Passed
Expression: length(Nil()) == 0
Evaluated: 0 == 0
julia> @test length(Cons(1, Cons(2, Cons(3, Nil())))) == 3
Test Passed
Expression: length(Cons(1,Cons(2,Cons(3,Nil())))) == 3
Evaluated: 3 == 3
下一步
这个玩具示例远非实现链接列表中所需的所有功能。例如,它缺少迭代接口。但是,它说明了如何使用 dispatch 编写简短明了的代码。