排程型別

在 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

這有利於多型性 。例如,我們可以通過定義兩個名為 NilCons 的不可變型別輕鬆建立連結串列 。這些名稱傳統上分別用於描述空列表和非空列表。

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 編寫簡短明瞭的程式碼。