排程簡介
我們可以使用::
語法來排程引數的型別 。
describe(n::Integer) = "integer $n"
describe(n::AbstractFloat) = "floating point $n"
用法:
julia> describe(10)
"integer 10"
julia> describe(1.0)
"floating point 1.0"
與許多通常提供靜態多重排程或動態單一排程的語言不同,Julia 具有完全動態的多重排程。也就是說,函式可以專用於多個引數。在為某些型別的操作定義專用方法時,這會派上用場,而對於其他型別則可以使用回退方法。
describe(n::Integer, m::Integer) = "integers n=$n and m=$m"
describe(n, m::Integer) = "only m=$m is an integer"
describe(n::Integer, m) = "only n=$n is an integer"
用法:
julia> describe(10, 'x')
"only n=10 is an integer"
julia> describe('x', 10)
"only m=10 is an integer"
julia> describe(10, 10)
"integers n=10 and m=10"
可選引數
Julia 允許函式採用可選引數。在幕後,這是作為多個排程的另一個特例實現的。例如,讓我們解決流行的 Fizz Buzz 問題 。預設情況下,我們將對 1:10
範圍內的數字執行此操作,但如有必要,我們將允許使用不同的值。我們還將允許不同的短語用於 Fizz
或 Buzz
。
function fizzbuzz(xs=1:10, fizz="Fizz", buzz="Buzz")
for i in xs
if i % 15 == 0
println(fizz, buzz)
elseif i % 3 == 0
println(fizz)
elseif i % 5 == 0
println(buzz)
else
println(i)
end
end
end
如果我們在 REPL 中檢查 fizzbuzz
,它說有四種方法。為每個允許的引數組合建立了一種方法。
julia> fizzbuzz
fizzbuzz (generic function with 4 methods)
julia> methods(fizzbuzz)
# 4 methods for generic function "fizzbuzz":
fizzbuzz() at REPL[96]:2
fizzbuzz(xs) at REPL[96]:2
fizzbuzz(xs, fizz) at REPL[96]:2
fizzbuzz(xs, fizz, buzz) at REPL[96]:2
我們可以驗證在沒有提供引數時使用我們的預設值:
julia> fizzbuzz()
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
但如果我們提供可選引數,則接受並尊重它們:
julia> fizzbuzz(5:8, "fuzz", "bizz")
bizz
fuzz
7
8
引數排程
通常情況下,函式應排程引數型別,例如 Vector{T}
或 Dict{K,V}
,但型別引數不固定。這種情況可以通過引數排程來處理:
julia> foo{T<:Number}(xs::Vector{T}) = @show xs .+ 1
foo (generic function with 1 method)
julia> foo(xs::Vector) = @show xs
foo (generic function with 2 methods)
julia> foo([1, 2, 3])
xs .+ 1 = [2,3,4]
3-element Array{Int64,1}:
2
3
4
julia> foo([1.0, 2.0, 3.0])
xs .+ 1 = [2.0,3.0,4.0]
3-element Array{Float64,1}:
2.0
3.0
4.0
julia> foo(["x", "y", "z"])
xs = String["x","y","z"]
3-element Array{String,1}:
"x"
"y"
"z"
人們可能只想簡單地寫一下 xs::Vector{Number}
。但這僅適用於型別明確為 Vector{Number}
的物件:
julia> isa(Number[1, 2], Vector{Number})
true
julia> isa(Int[1, 2], Vector{Number})
false
這是由於引數不變性 :物件 Int[1, 2]
不是 Vector{Number}
,因為它只能包含 Int
s,而 Vector{Number}
可以包含任何型別的數字。
編寫通用程式碼
Dispatch 是一個非常強大的功能,但通常最好編寫適用於所有型別的通用程式碼,而不是為每種型別專門編寫程式碼。編寫通用程式碼可避免程式碼重複。
例如,這裡是計算整數向量的平方和的程式碼:
function sumsq(v::Vector{Int})
s = 0
for x in v
s += x ^ 2
end
s
end
但是此程式碼僅適用於 Int
s 的向量。它不適用於 UnitRange
:
julia> sumsq(1:10)
ERROR: MethodError: no method matching sumsq(::UnitRange{Int64})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
它不適用於 Vector{Float64}
:
julia> sumsq([1.0, 2.0])
ERROR: MethodError: no method matching sumsq(::Array{Float64,1})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
編寫這個 sumsq
函式的更好方法應該是
function sumsq(v::AbstractVector)
s = zero(eltype(v))
for x in v
s += x ^ 2
end
s
end
這將適用於上面列出的兩種情況。但是在某種意義上,有些集合我們可能想要總結那些不是向量的正方形。例如,
julia> sumsq(take(countfrom(1), 100))
ERROR: MethodError: no method matching sumsq(::Base.Take{Base.Count{Int64}})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
sumsq(::AbstractArray{T,1}) at REPL[11]:2
表明我們不能對惰性迭代的平方求和。
更通用的實現很簡單
function sumsq(v)
s = zero(eltype(v))
for x in v
s += x ^ 2
end
s
end
哪個適用於所有情況:
julia> sumsq(take(countfrom(1), 100))
338350
這是最慣用的 Julia 程式碼,可以處理各種情況。在其他一些語言中,刪除型別註釋可能會影響效能,但在 Julia 中則不是這樣; 只有型別穩定性對效能很重要。