構(gòu)造函數(shù)[1]是構(gòu)造新對(duì)象,即新復(fù)合類(lèi)型實(shí)例的函數(shù)。構(gòu)造類(lèi)型對(duì)象:
type Foo
bar
baz
end
julia> foo = Foo(1,2)
Foo(1,2)
julia> foo.bar
1
julia> foo.baz
2
遞歸數(shù)據(jù)結(jié)構(gòu) ,尤其是自引用的數(shù)據(jù)結(jié)構(gòu),常需要先構(gòu)造為非完整狀態(tài),再按步驟將其完善。我們有時(shí)也可能希望用更少或不同類(lèi)型的參數(shù)更方便的構(gòu)造對(duì)象。Julia 的構(gòu)造函數(shù)可以讓包括這些在內(nèi)的各種需求得到滿(mǎn)足。
[1] :關(guān)于命名:盡管“構(gòu)造函數(shù)”通常被用來(lái)描述創(chuàng)建新對(duì)象的函數(shù),它也經(jīng)常被濫用于特定的構(gòu)造方法。通常情況下,可以很容易地從上下文推斷出到底是“構(gòu)造函數(shù)”還是“構(gòu)造方法”。
構(gòu)造函數(shù)與 Julia 中的其它函數(shù)一樣,它的行為取決于它全部方法的行為的組合。因此,你可以通過(guò)定義新方法來(lái)給構(gòu)造函數(shù)增加新性能。下例給 Foo
添加了新構(gòu)造方法,僅輸入一個(gè)參數(shù),將該參數(shù)值賦給 bar
和 baz
域:
Foo(x) = Foo(x,x)
julia> Foo(1)
Foo(1,1)
添加 Foo
的零參構(gòu)造方法,給 bar
和 baz
域賦默認(rèn)值:
Foo() = Foo(0)
julia> Foo()
Foo(0,0)
這種追加的構(gòu)造方法被稱(chēng)為 外部 構(gòu)造方法。它僅能通過(guò)提供默認(rèn)值的方式,調(diào)用其它構(gòu)造方法來(lái)構(gòu)造實(shí)例。
內(nèi)部 構(gòu)造方法與外部構(gòu)造方法類(lèi)似,但有兩個(gè)區(qū)別:
new
函數(shù),來(lái)構(gòu)造聲明塊的類(lèi)型的對(duì)象例如,要聲明一個(gè)保存實(shí)數(shù)對(duì)的類(lèi)型,且第一個(gè)數(shù)不大于第二個(gè)數(shù):
type OrderedPair
x::Real
y::Real
OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
end
僅當(dāng) x <= y
時(shí),才會(huì)構(gòu)造 OrderedPair
對(duì)象:
julia> OrderedPair(1,2)
OrderedPair(1,2)
julia> OrderedPair(2,1)
ERROR: out of order
in OrderedPair at none:5
所有的外部構(gòu)造方法,最終都會(huì)調(diào)用內(nèi)部構(gòu)造方法。
當(dāng)然,如果類(lèi)型被聲明為 immutable
,它的構(gòu)造函數(shù)的結(jié)構(gòu)就不能變了。這對(duì)判斷一個(gè)類(lèi)型是否應(yīng)該是 immutable 時(shí)很重要。
如果定義了內(nèi)部構(gòu)造方法,Julia 將不再提供默認(rèn)的構(gòu)造方法。默認(rèn)的構(gòu)造方法等價(jià)于一個(gè)自定義內(nèi)部構(gòu)造方法,它將對(duì)象的所有域作為參數(shù)(如果對(duì)應(yīng)域有類(lèi)型,應(yīng)為具體類(lèi)型),傳遞給 new
,最后返回結(jié)果對(duì)象:
type Foo
bar
baz
Foo(bar,baz) = new(bar,baz)
end
這個(gè)聲明與前面未指明內(nèi)部構(gòu)造方法的 Foo
是等價(jià)的。下面兩者也是等價(jià)的,一個(gè)使用默認(rèn)構(gòu)造方法,一個(gè)寫(xiě)明了構(gòu)造方法:
type T1
x::Int64
end
type T2
x::Int64
T2(x) = new(x)
end
julia> T1(1)
T1(1)
julia> T2(1)
T2(1)
julia> T1(1.0)
T1(1)
julia> T2(1.0)
T2(1)
內(nèi)部構(gòu)造方法能不寫(xiě)就不寫(xiě)。提供默認(rèn)值之類(lèi)的事兒,應(yīng)該寫(xiě)成外部構(gòu)造方法,由它們調(diào)用內(nèi)部構(gòu)造方法。
考慮如下遞歸類(lèi)型聲明:
type SelfReferential
obj::SelfReferential
end
如果 a
是 SelfReferential
的實(shí)例,則可以如下構(gòu)造第二個(gè)實(shí)例:
b = SelfReferential(a)
但是,當(dāng)沒(méi)有任何實(shí)例來(lái)為 obj
域提供有效值時(shí),如何構(gòu)造第一個(gè)實(shí)例呢?唯一的解決方法是構(gòu)造 obj
域未賦值的 SelfReferential
部分初始化實(shí)例,使用這個(gè)實(shí)例作為另一個(gè)實(shí)例(如它本身)中 obj
域的有效值。
構(gòu)造部分初始化對(duì)象時(shí),Julia 允許調(diào)用 new
函數(shù)來(lái)處理比該類(lèi)型域個(gè)數(shù)少的參數(shù),返回部分域未初始化的對(duì)象。這時(shí),內(nèi)部構(gòu)造函數(shù)可以使用這個(gè)不完整的對(duì)象,并在返回之前完成它的初始化。下例中,我們定義 SelfReferential
類(lèi)型時(shí),使用零參內(nèi)部構(gòu)造方法,返回一個(gè) obj
域指向它本身的實(shí)例:
type SelfReferential
obj::SelfReferential
SelfReferential() = (x = new(); x.obj = x)
end
此構(gòu)造方法可以運(yùn)行并構(gòu)造自引對(duì)象:
julia> x = SelfReferential();
julia> is(x, x)
true
julia> is(x, x.obj)
true
julia> is(x, x.obj.obj)
true
內(nèi)部構(gòu)造方法最好返回完全初始化的對(duì)象,但也可以返回部分初始化對(duì)象:
julia> type Incomplete
xx
Incomplete() = new()
end
julia> z = Incomplete();
盡管可以構(gòu)造未初始化域的對(duì)象,但讀取未初始化的引用會(huì)報(bào)錯(cuò):
julia> z.xx
ERROR: access to undefined reference
這避免了持續(xù)檢查 null
值。但是,所有對(duì)象的域都是引用。Julia 認(rèn)為一些類(lèi)型是“普通數(shù)據(jù)”,即他們的數(shù)據(jù)都是獨(dú)立的,都不引用其他的對(duì)象。普通數(shù)據(jù)類(lèi)型是由位類(lèi)型或者其他普通數(shù)據(jù)類(lèi)型的不可變數(shù)據(jù)結(jié)構(gòu)所構(gòu)成的(例如 Int
)。普通數(shù)據(jù)類(lèi)型的初始內(nèi)容是未定義的: ::
julia> type HasPlain
n::Int
HasPlain() = new()
end
julia> HasPlain()
HasPlain(438103441441)
普通數(shù)據(jù)類(lèi)型所構(gòu)成的數(shù)組具有相同的行為。
可以在內(nèi)部構(gòu)造方法中,將不完整的對(duì)象傳遞給其它函數(shù),來(lái)委托完成全部初始化:
type Lazy
xx
Lazy(v) = complete_me(new(), v)
end
如果 complete_me
或其它被調(diào)用的函數(shù)試圖在初始化 Lazy
對(duì)象的 xx
域之前讀取它,將會(huì)立即報(bào)錯(cuò)。
參數(shù)化構(gòu)造方法的例子:
julia> type Point{T<:Real}
x::T
y::T
end
## implicit T ##
julia> Point(1,2)
Point{Int64}(1,2)
julia> Point(1.0,2.5)
Point{Float64}(1.0,2.5)
julia> Point(1,2.5)
ERROR: `Point{T<:Real}` has no method matching Point{T<:Real}(::Int64, ::Float64)
## explicit T ##
julia> Point{Int64}(1,2)
Point{Int64}(1,2)
julia> Point{Int64}(1.0,2.5)
ERROR: InexactError()
julia> Point{Float64}(1.0,2.5)
Point{Float64}(1.0,2.5)
julia> Point{Float64}(1,2)
Point{Float64}(1.0,2.0)
上面的參數(shù)化構(gòu)造方法等價(jià)于下面的聲明:
type Point{T<:Real}
x::T
y::T
Point(x,y) = new(x,y)
end
Point{T<:Real}(x::T, y::T) = Point{T}(x,y)
內(nèi)部構(gòu)造方法只定義 Point{T}
的方法,而非 Point
的構(gòu)造函數(shù)的方法。 Point
不是具體類(lèi)型,不能有內(nèi)部構(gòu)造方法。外部構(gòu)造方法定義了 Point
的構(gòu)造方法。
可以將整數(shù)值 1
“提升”為浮點(diǎn)數(shù) 1.0
,來(lái)完成構(gòu)造:
julia> Point(x::Int64, y::Float64) = Point(convert(Float64,x),y);
這樣下例就可以正常運(yùn)行:
julia> Point(1,2.5)
Point{Float64}(1.0,2.5)
julia> typeof(ans)
Point{Float64} (constructor with 1 method)
但下例仍會(huì)報(bào)錯(cuò):
julia> Point(1.5,2)
ERROR: `Point{T<:Real}` has no method matching Point{T<:Real}(::Float64, ::Int64)
其實(shí)只需定義下列外部構(gòu)造方法:
julia> Point(x::Real, y::Real) = Point(promote(x,y)...);
promote
函數(shù)將它的所有參數(shù)轉(zhuǎn)換為相同類(lèi)型?,F(xiàn)在,所有的實(shí)數(shù)參數(shù)都可以正常運(yùn)行:
julia> Point(1.5,2)
Point{Float64}(1.5,2.0)
julia> Point(1,1//2)
Point{Rational{Int64}}(1//1,1//2)
julia> Point(1.0,1//2)
Point{Float64}(1.0,0.5)
下面是 rational.jl 文件的開(kāi)頭部分,它實(shí)現(xiàn)了 Julia 的分?jǐn)?shù):
immutable Rational{T<:Integer} <: Real
num::T
den::T
function Rational(num::T, den::T)
if num == 0 && den == 0
error("invalid rational: 0//0")
end
g = gcd(den, num)
num = div(num, g)
den = div(den, g)
new(num, den)
end
end
Rational{T<:Integer}(n::T, d::T) = Rational{T}(n,d)
Rational(n::Integer, d::Integer) = Rational(promote(n,d)...)
Rational(n::Integer) = Rational(n,one(n))
//(n::Integer, d::Integer) = Rational(n,d)
//(x::Rational, y::Integer) = x.num // (x.den*y)
//(x::Integer, y::Rational) = (x*y.den) // y.num
//(x::Complex, y::Real) = complex(real(x)//y, imag(x)//y)
//(x::Real, y::Complex) = x*y'//real(y*y')
function //(x::Complex, y::Complex)
xy = x*y'
yy = real(y*y')
complex(real(xy)//yy, imag(xy)//yy)
end
復(fù)數(shù)分?jǐn)?shù)的例子:
julia> (1 + 2im)//(1 - 2im)
-3//5 + 4//5*im
julia> typeof(ans)
Complex{Rational{Int64}} (constructor with 1 method)
julia> ans <: Complex{Rational}
false
更多建議: