Constructors
Yapıcılar [1] yeni nesneler oluşturan fonksiyonlardır - özellikle, Composite Types örnekleri. Julia'da, tür nesneleri de yapıcı fonksiyonlar olarak hizmet eder: bir argüman demetine fonksiyon olarak uygulandıklarında kendilerinin yeni örneklerini oluştururlar. Bu, bileşik türler tanıtıldığında kısaca bahsedilmişti. Örneğin:
julia> struct Foo
bar
baz
end
julia> foo = Foo(1, 2)
Foo(1, 2)
julia> foo.bar
1
julia> foo.baz
2
Birçok tür için, yeni nesneler oluşturmak için alan değerlerini bir araya getirmek yeterlidir. Ancak, bazı durumlarda bileşik nesneler oluştururken daha fazla işlevsellik gereklidir. Bazen, argümanları kontrol ederek veya dönüştürerek, invariants zorunlu hale getirilmelidir. Recursive data structures, özellikle kendine referans verenler, önce eksik bir durumda oluşturulmadan ve ardından programatik olarak bütün hale getirilmeden temiz bir şekilde oluşturulamaz. Bazen, nesneleri sahip oldukları alanlardan daha az veya farklı türde parametrelerle oluşturmak sadece pratik bir durumdur. Julia'nın nesne oluşturma sistemi, bu durumların hepsini ve daha fazlasını ele alır.
Outer Constructor Methods
Bir yapıcı, Julia'daki diğer herhangi bir işlev gibi, genel davranışı yöntemlerinin birleşik davranışıyla tanımlanır. Bu nedenle, yeni yöntemler tanımlayarak bir yapıcıya işlevsellik ekleyebilirsiniz. Örneğin, Foo
nesneleri için yalnızca bir argüman alan ve verilen değeri hem bar
hem de baz
alanları için kullanan bir yapıcı yöntemi eklemek istediğinizi varsayalım. Bu basit:
julia> Foo(x) = Foo(x,x)
Foo
julia> Foo(1)
Foo(1, 1)
Sıfır argümanlı bir Foo
yapıcı metodu ekleyebilir ve hem bar
hem de baz
alanları için varsayılan değerler sağlayabilirsiniz:
julia> Foo() = Foo(0)
Foo
julia> Foo()
Foo(0, 0)
Burada sıfır argümanlı yapıcı yöntem, tek argümanlı yapıcı yöntemi çağırır; bu da otomatik olarak sağlanan iki argümanlı yapıcı yöntemi çağırır. Çok yakında netleşecek nedenlerden dolayı, normal yöntemler olarak tanımlanan ek yapıcı yöntemler dış yapıcı yöntemler olarak adlandırılır. Dış yapıcı yöntemler yalnızca başka bir yapıcı yöntemi, örneğin otomatik olarak sağlanan varsayılan olanları çağırarak yeni bir örnek oluşturabilir.
Inner Constructor Methods
Dış yapıcı yöntemler, nesne oluşturma için ek kolaylık yöntemleri sağlama sorununu ele almakta başarılı olsa da, bu bölümün girişinde bahsedilen diğer iki kullanım durumunu ele almakta başarısızdır: değişmezlikleri zorlamak ve kendine referans veren nesnelerin oluşturulmasına izin vermek. Bu sorunlar için iç yapıcı yöntemlere ihtiyaç vardır. Bir iç yapıcı yöntemi, iki fark dışında dış yapıcı yöntemine benzer:
- Bu, normal yöntemler gibi dışarıda değil, bir tür bildirim bloğunun içinde tanımlanmıştır.
- Özel olarak mevcut olan
new
adlı bir işleve erişimi vardır; bu işlev, bloğun türünde nesneler oluşturur.
Örneğin, birinin ilk sayının ikinci sayıdan büyük olmaması koşuluna tabi olan bir çift gerçek sayı tutan bir tür tanımlamak istediğini varsayalım. Bunu şu şekilde tanımlayabiliriz:
julia> struct OrderedPair
x::Real
y::Real
OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
end
Artık OrderedPair
nesneleri yalnızca x <= y
olacak şekilde oluşturulabilir:
julia> OrderedPair(1, 2)
OrderedPair(1, 2)
julia> OrderedPair(2,1)
ERROR: out of order
Stacktrace:
[1] error at ./error.jl:33 [inlined]
[2] OrderedPair(::Int64, ::Int64) at ./none:4
[3] top-level scope
Eğer tür mutable
olarak tanımlanmış olsaydı, alan değerlerini doğrudan değiştirerek bu değişmezliği ihlal edebilirdiniz. Elbette, bir nesnenin iç yapısıyla izinsiz oynamak kötü bir uygulamadır. Siz (veya başka biri) daha sonra ek dış yapıcı yöntemler sağlayabilirsiniz, ancak bir tür tanımlandıktan sonra daha fazla iç yapıcı yöntem eklemenin bir yolu yoktur. Dış yapıcı yöntemler yalnızca diğer yapıcı yöntemleri çağırarak nesneler oluşturabileceğinden, nihayetinde bir nesne oluşturmak için bazı iç yapıcıların çağrılması gerekir. Bu, tanımlanan türün tüm nesnelerinin, türle sağlanan iç yapıcı yöntemlerden birine yapılan bir çağrı ile var olması gerektiğini garanti eder ve böylece bir türün değişmezliklerinin bir dereceye kadar uygulanmasını sağlar.
Eğer herhangi bir iç yapıcı yöntemi tanımlanmışsa, varsayılan bir yapıcı yöntemi sağlanmaz: ihtiyacınız olan tüm iç yapıcıları kendiniz sağladığınız varsayılır. Varsayılan yapıcı, nesnenin tüm alanlarını parametre olarak alan kendi iç yapıcı yönteminizi yazmakla eşdeğerdir (karşılık gelen alanın bir türü varsa, doğru türde olma kısıtlaması ile) ve bunları new
'e geçirerek elde edilen nesneyi döndürür:
julia> struct Foo
bar
baz
Foo(bar,baz) = new(bar,baz)
end
Bu beyan, açık bir iç yapıcı yöntemi olmadan Foo
türünün önceki tanımıyla aynı etkiye sahiptir. Aşağıdaki iki tür eşdeğerdir - biri varsayılan bir yapıcı ile, diğeri açık bir yapıcı ile:
julia> struct T1
x::Int64
end
julia> struct 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)
İç yapıcı yöntemlerin sayısını mümkün olduğunca az tutmak iyi bir uygulamadır: yalnızca tüm argümanları açıkça alan ve temel hata kontrolü ve dönüşümünü zorlayanlar. Varsayılan değerler veya yardımcı dönüşümler sağlayan ek kolaylık yapıcı yöntemler, ağır yükü üstlenmek için iç yapıcıları çağıran dış yapıcılar olarak sağlanmalıdır. Bu ayrım genellikle oldukça doğaldır.
Incomplete Initialization
Son olarak ele alınmamış olan nihai problem, kendine referans veren nesnelerin veya daha genel olarak, özyinelemeli veri yapılarının inşasıdır. Temel zorluğun hemen belirgin olmayabileceğini göz önünde bulundurarak, bunu kısaca açıklayalım. Aşağıdaki özyinelemeli tür bildirimini düşünün:
julia> mutable struct SelfReferential
obj::SelfReferential
end
Bu tür, yeterince zararsız görünebilir, ta ki bir örneğini nasıl oluşturacağınızı düşünene kadar. Eğer a
, SelfReferential
'ın bir örneği ise, o zaman ikinci bir örnek şu çağrı ile oluşturulabilir:
julia> b = SelfReferential(a)
Ama bir obj
alanı için geçerli bir değer sağlayacak hiçbir örnek yokken ilk örneği nasıl oluştururuz? Tek çözüm, obj
alanı atanmış olmayan eksik bir SelfReferential
örneği oluşturmaya izin vermek ve bu eksik örneği başka bir örneğin, örneğin kendisinin obj
alanı için geçerli bir değer olarak kullanmaktır.
Tamamlanmamış olarak başlatılmış nesnelerin oluşturulmasına izin vermek için, Julia new
fonksiyonunun, türün sahip olduğu alan sayısından daha az alan ile çağrılmasına izin verir; bu, belirtilmemiş alanların başlatılmamış olduğu bir nesne döndürür. İç yapıcı yöntem, tamamlanmamış nesneyi kullanarak, döndürmeden önce başlatmasını tamamlayabilir. İşte, örneğin, SelfReferential
türünü tanımlamanın bir başka denemesi; bu sefer, obj
alanlarının kendilerine işaret ettiği örnekler döndüren sıfır argümanlı bir iç yapıcı kullanarak:
julia> mutable struct SelfReferential
obj::SelfReferential
SelfReferential() = (x = new(); x.obj = x)
end
Bu yapıcının çalıştığını ve nesnelerin gerçekten kendine referans veren nesneler olarak oluşturulduğunu doğrulayabiliriz:
julia> x = SelfReferential();
julia> x === x
true
julia> x === x.obj
true
julia> x === x.obj.obj
true
İç içe bir yapıcıdan tamamen başlatılmış bir nesne döndürmenin genellikle iyi bir fikir olduğu doğru olsa da, eksik başlatılmış nesneleri döndürmek de mümkündür:
julia> mutable struct Incomplete
data
Incomplete() = new()
end
julia> z = Incomplete();
Uninitialized alanlara erişim, hemen bir hata oluşturur; ancak, başlatılmamış alanlarla nesneler oluşturmanıza izin verilir:
julia> z.data
ERROR: UndefRefError: access to undefined reference
Bu, sürekli olarak null
değerlerini kontrol etme ihtiyacını ortadan kaldırır. Ancak, tüm nesne alanları referans değildir. Julia, bazı türleri "sade veri" olarak kabul eder; bu, verilerinin tamamının kendi içinde bulunduğu ve diğer nesnelere referans vermediği anlamına gelir. Sade veri türleri, ilkel türleri (örneğin, Int
) ve diğer sade veri türlerinin değişmez yapılarını içerir (bkz. ayrıca: isbits
, isbitstype
). Bir sade veri türünün başlangıç içeriği belirsizdir:
julia> struct HasPlain
n::Int
HasPlain() = new()
end
julia> HasPlain()
HasPlain(438103441441)
Düz veri türlerinin dizileri aynı davranışı sergiler.
Tamamlanmamış nesneleri iç yapıcılardan diğer işlevlere geçirebilir ve tamamlanmalarını devredebilirseniz:
julia> mutable struct Lazy
data
Lazy(v) = complete_me(new(), v)
end
Tam olarak tamamlanmamış nesnelerden dönen nesnelerle olduğu gibi, eğer complete_me
veya onun çağrılan herhangi biri Lazy
nesnesinin data
alanına, başlatılmadan önce erişmeye çalışırsa, hemen bir hata fırlatılacaktır.
Parametric Constructors
Parametrik türler, yapıcı hikayesine birkaç karmaşıklık ekler. Parametric Types'dan hatırlayalım ki, varsayılan olarak, parametrik bileşik türlerin örnekleri ya açıkça verilen tür parametreleri ile ya da yapıcıya verilen argümanların türleri tarafından ima edilen tür parametreleri ile oluşturulabilir. İşte bazı örnekler:
julia> struct Point{T<:Real}
x::T
y::T
end
julia> Point(1,2) ## implicit T ##
Point{Int64}(1, 2)
julia> Point(1.0,2.5) ## implicit T ##
Point{Float64}(1.0, 2.5)
julia> Point(1,2.5) ## implicit T ##
ERROR: MethodError: no method matching Point(::Int64, ::Float64)
The type `Point` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
Point(::T, ::T) where T<:Real at none:2
julia> Point{Int64}(1, 2) ## explicit T ##
Point{Int64}(1, 2)
julia> Point{Int64}(1.0,2.5) ## explicit T ##
ERROR: InexactError: Int64(2.5)
Stacktrace:
[...]
julia> Point{Float64}(1.0, 2.5) ## explicit T ##
Point{Float64}(1.0, 2.5)
julia> Point{Float64}(1,2) ## explicit T ##
Point{Float64}(1.0, 2.0)
Gördüğünüz gibi, açık tür parametreleri ile yapılan yapıcı çağrılarında, argümanlar varsayılan alan türlerine dönüştürülür: Point{Int64}(1,2)
çalışır, ancak Point{Int64}(1.0,2.5)
InexactError
hatasını verirken 2.5
'i Int64
'ye dönüştürmeye çalışır. Tür, yapıcı çağrısındaki argümanlar tarafından varsayıldığında, Point(1,2)
gibi, o zaman argümanların türleri uyuşmalıdır - aksi takdirde T
belirlenemez - ancak eşleşen türdeki herhangi bir gerçek argüman çifti genel Point
yapıcısına verilebilir.
Burada gerçekten olan şey, Point
, Point{Float64}
ve Point{Int64}
'ün hepsinin farklı yapıcı fonksiyonlar olmasıdır. Aslında, Point{T}
her T
tipi için ayrı bir yapıcı fonksiyondur. Herhangi bir açıkça sağlanmış iç yapıcı olmadan, Point{T<:Real}
bileşen türünün beyanı, her olası T<:Real
tipi için, normal parametreli varsayılan iç yapıcılar gibi davranan otomatik olarak bir iç yapıcı Point{T}
sağlar. Ayrıca, aynı türde olmalıdırlar, gerçek argüman çiftlerini alan tek bir genel dış Point
yapıcısı sağlar. Bu yapıcıların otomatik sağlanması, aşağıdaki açık beyanla eşdeğerdir:
julia> struct Point{T<:Real}
x::T
y::T
Point{T}(x,y) where {T<:Real} = new(x,y)
end
julia> Point(x::T, y::T) where {T<:Real} = Point{T}(x,y);
Herhangi bir tanımın, işlediği yapıcı çağrısının biçimine benzediğini unutmayın. Point{Int64}(1,2)
çağrısı, struct
bloğu içindeki Point{T}(x,y)
tanımını tetikleyecektir. Öte yandan, dış yapıcı bildirimi, yalnızca aynı gerçek türdeki değer çiftlerine uygulanan genel Point
yapıcısı için bir yöntem tanımlar. Bu bildirim, açık tür parametreleri olmadan yapılan yapıcı çağrılarının, Point(1,2)
ve Point(1.0,2.5)
gibi çalışmasını sağlar. Yöntem bildirimi, argümanları aynı türde olma ile kısıtladığından, farklı türde argümanlar içeren Point(1,2.5)
gibi çağrılar "hiçbir yöntem" hatalarına yol açar.
Varsayıyoruz ki Point(1,2.5)
yapıcı çağrısını, tam sayı değeri 1
'i kayan nokta değeri 1.0
'a "terfi ettirerek" çalışır hale getirmek istiyoruz. Bunu başarmanın en basit yolu, aşağıdaki ek dış yapıcı yöntemini tanımlamaktır:
julia> Point(x::Int64, y::Float64) = Point(convert(Float64,x),y);
Bu yöntem, convert
fonksiyonunu kullanarak x
'i açıkça Float64
'ya dönüştürür ve ardından her iki argümanın da 4d61726b646f776e2e436f64652822222c2022466c6f617436342229_40726566
olduğu durum için genel yapıcıya inşaatı devreder. Bu yöntem tanımı ile daha önce MethodError
olan şey artık Point{Float64}
türünde bir nokta oluşturmayı başarıyla gerçekleştirir:
julia> p = Point(1,2.5)
Point{Float64}(1.0, 2.5)
julia> typeof(p)
Point{Float64}
Ancak, diğer benzer aramalar hala çalışmıyor:
julia> Point(1.5,2)
ERROR: MethodError: no method matching Point(::Float64, ::Int64)
The type `Point` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
Point(::T, !Matched::T) where T<:Real
@ Main none:1
Point(!Matched::Int64, !Matched::Float64)
@ Main none:1
Stacktrace:
[...]
For a more general way to make all such calls work sensibly, see Conversion and Promotion. At the risk of spoiling the suspense, we can reveal here that all it takes is the following outer method definition to make all calls to the general Point
constructor work as one would expect:
julia> Point(x::Real, y::Real) = Point(promote(x,y)...);
promote
fonksiyonu tüm argümanlarını ortak bir türe dönüştürür - bu durumda Float64
. Bu yöntem tanımı ile Point
yapıcısı argümanlarını, +
gibi sayısal operatörlerin yaptığı gibi yükseltir ve tüm gerçek sayılar için çalışır:
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)
Böylece, Julia'da varsayılan olarak sağlanan örtük tür parametreli yapıcılar oldukça katı olsa da, bunların daha rahat ama mantıklı bir şekilde davranmasını sağlamak oldukça kolaydır. Ayrıca, yapıcılar tür sisteminin, yöntemlerin ve çoklu dağıtımın tüm gücünden yararlanabildiğinden, karmaşık davranışları tanımlamak genellikle oldukça basittir.
Case Study: Rational
Belki de bu parçaları bir araya getirmenin en iyi yolu, bir parametreli bileşik tür ve onun yapıcı yöntemleri ile ilgili gerçek bir dünya örneği sunmaktır. Bu amaçla, Julia'nın yerleşik Rational
türüne benzer kendi rasyonel sayı türümüz OurRational
'ı uyguluyoruz, tanımlandığı rational.jl
:
julia> struct OurRational{T<:Integer} <: Real
num::T
den::T
function OurRational{T}(num::T, den::T) where T<:Integer
if num == 0 && den == 0
error("invalid rational: 0//0")
end
num = flipsign(num, den)
den = flipsign(den, den)
g = gcd(num, den)
num = div(num, g)
den = div(den, g)
new(num, den)
end
end
julia> OurRational(n::T, d::T) where {T<:Integer} = OurRational{T}(n,d)
OurRational
julia> OurRational(n::Integer, d::Integer) = OurRational(promote(n,d)...)
OurRational
julia> OurRational(n::Integer) = OurRational(n,one(n))
OurRational
julia> ⊘(n::Integer, d::Integer) = OurRational(n,d)
⊘ (generic function with 1 method)
julia> ⊘(x::OurRational, y::Integer) = x.num ⊘ (x.den*y)
⊘ (generic function with 2 methods)
julia> ⊘(x::Integer, y::OurRational) = (x*y.den) ⊘ y.num
⊘ (generic function with 3 methods)
julia> ⊘(x::Complex, y::Real) = complex(real(x) ⊘ y, imag(x) ⊘ y)
⊘ (generic function with 4 methods)
julia> ⊘(x::Real, y::Complex) = (x*y') ⊘ real(y*y')
⊘ (generic function with 5 methods)
julia> function ⊘(x::Complex, y::Complex)
xy = x*y'
yy = real(y*y')
complex(real(xy) ⊘ yy, imag(xy) ⊘ yy)
end
⊘ (generic function with 6 methods)
İlk satır – struct OurRational{T<:Integer} <: Real
– OurRational
'ın bir tamsayı türü parametresi aldığını ve kendisinin de bir reel tür olduğunu belirtir. Alan bildirimleri num::T
ve den::T
, bir OurRational{T}
nesnesinde tutulan verilerin T
türünde bir çift tamsayı olduğunu, birinin rasyonel değerin payını ve diğerinin paydasını temsil ettiğini gösterir.
Şimdi işler ilginçleşiyor. OurRational
'ın, num
ve den
'in ikisinin de sıfır olmadığını kontrol eden ve her rasyonel sayının "en düşük terimlerde" ve negatif olmayan bir payda ile oluşturulmasını sağlayan tek bir iç yapıcı yöntemi vardır. Bu, önce paydanın negatif olması durumunda pay ve paydanın işaretlerini tersine çevirerek gerçekleştirilir. Ardından, her ikisi de en büyük ortak bölenleri ile bölünür (gcd
her zaman, argümanlarının işaretine bakılmaksızın, negatif olmayan bir sayı döner). Bu, OurRational
için tek iç yapıcı olduğundan, OurRational
nesnelerinin her zaman bu normalleştirilmiş biçimde oluşturulacağından emin olabiliriz.
OurRational
ayrıca kullanım kolaylığı için birkaç dış yapıcı yöntem sunar. İlki, pay ve paydanın aynı türde olduğu durumlarda tür parametresi T
'yi pay ve paydanın türünden çıkaran "standart" genel yapıcıdır. İkincisi, verilen pay ve payda değerlerinin farklı türlere sahip olduğu durumlarda uygulanır: bunları ortak bir türe yükseltir ve ardından eşleşen türdeki argümanlar için dış yapıcıya inşaat delegasyonu yapar. Üçüncü dış yapıcı, tam sayı değerlerini, payda olarak 1
değeri sağlayarak rasyonellere dönüştürür.
Dış yapıcı tanımlamalarını takiben, ⊘
operatörü için bir dizi yöntem tanımladık; bu, rasyonelleri yazmak için bir sözdizimi sağlar (örneğin, 1 ⊘ 2
). Julia'nın Rational
tipi bu amaç için //
operatörünü kullanır. Bu tanımlamalardan önce, ⊘
tamamen tanımsız bir operatördür; yalnızca sözdizimi vardır ve anlamı yoktur. Sonrasında, Rational Numbers'da açıklandığı gibi davranır – tüm davranışı bu birkaç satırda tanımlanmıştır. ⊘
'nin infiks kullanımı, Julia'nın infiks operatörler olarak tanınan bir dizi sembole sahip olmasından dolayı çalışır. İlk ve en temel tanım, a ⊘ b
ifadesinin, a
ve b
tam sayılar olduğunda OurRational
yapıcısını uygulayarak bir OurRational
oluşturmasını sağlar. ⊘
'nin operandlarından biri zaten bir rasyonel sayı olduğunda, sonuç oranı için yeni bir rasyonel oluşturma işlemi biraz farklıdır; bu davranış aslında bir rasyoneli bir tam sayıyla bölmeye eşdeğerdir. Son olarak, ⊘
'yi karmaşık integral değerlere uygulamak, Complex{<:OurRational}
örneğini oluşturur – reel ve sanal kısımları rasyonel olan bir karmaşık sayı:
julia> z = (1 + 2im) ⊘ (1 - 2im);
julia> typeof(z)
Complex{OurRational{Int64}}
julia> typeof(z) <: Complex{<:OurRational}
true
Böylece, ⊘
operatörü genellikle OurRational
örneği döndürse de, eğer argümanlarından biri karmaşık tam sayılardan biriyse, bunun yerine Complex{<:OurRational}
örneği döndürecektir. İlgilenen okuyucunun rational.jl
belgesini gözden geçirmesi önerilir: kısa, kendi içinde yeterli ve tam bir temel Julia türünü uygulamaktadır.
Outer-only constructors
Gördüğümüz gibi, tipik bir parametrik tür, tip parametreleri bilindiğinde çağrılan iç yapıcılara sahiptir; örneğin, Point{Int}
için geçerlidir ancak Point
için geçerli değildir. İsteğe bağlı olarak, tip parametrelerini otomatik olarak belirleyen dış yapıcılar eklenebilir; örneğin, Point(1,2)
çağrısından Point{Int}
oluşturmak. Dış yapıcılar, örnekleri gerçekten oluşturmak için iç yapıcıları çağırır. Ancak, bazı durumlarda, belirli tip parametrelerinin manuel olarak talep edilmesini istemediğiniz için iç yapıcılar sağlamamak daha iyi olabilir.
Örneğin, bir vektörü ve toplamının doğru bir temsilini saklayan bir tür tanımladığımızı varsayalım:
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
end
julia> SummedArray(Int32[1; 2; 3], Int32(6))
SummedArray{Int32, Int32}(Int32[1, 2, 3], 6)
Sorun, S
'nin T
'den daha büyük bir tür olmasını istememizdir, böylece daha az bilgi kaybıyla birçok öğeyi toplayabiliriz. Örneğin, T
Int32
olduğunda, S
'nin Int64
olmasını isteriz. Bu nedenle, kullanıcının SummedArray{Int32,Int32}
türünden örnekler oluşturmasına izin veren bir arayüzden kaçınmak istiyoruz. Bunu yapmanın bir yolu, yalnızca SummedArray
için bir yapıcı sağlamaktır, ancak struct
tanım bloğu içinde varsayılan yapıcıların oluşturulmasını engellemektir:
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
function SummedArray(a::Vector{T}) where T
S = widen(T)
new{T,S}(a, sum(S, a))
end
end
julia> SummedArray(Int32[1; 2; 3], Int32(6))
ERROR: MethodError: no method matching SummedArray(::Vector{Int32}, ::Int32)
The type `SummedArray` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
SummedArray(::Vector{T}) where T
@ Main none:4
Stacktrace:
[...]
Bu yapıcı, SummedArray(a)
sözdizimi ile çağrılacaktır. new{T,S}
sözdizimi, oluşturulacak tür için parametrelerin belirtilmesine olanak tanır; yani bu çağrı SummedArray{T,S}
döndürecektir. new{T,S}
herhangi bir yapıcı tanımında kullanılabilir, ancak kolaylık olması açısından new{}
için parametreler, mümkün olduğunda oluşturulan türden otomatik olarak türetilir.
Constructors are just callable objects
Herhangi bir türdeki bir nesne, bir yöntem tanımlayarak made callable olabilir. Bu, türleri de içerir, yani Type
türünde nesneler; ve yapıcılar aslında yalnızca çağrılabilir tür nesneleri olarak görülebilir. Örneğin, Bool
üzerinde tanımlanmış birçok yöntem ve onun çeşitli üst türleri vardır:
julia> methods(Bool)
# 10 methods for type constructor:
[1] Bool(x::BigFloat)
@ Base.MPFR mpfr.jl:393
[2] Bool(x::Float16)
@ Base float.jl:338
[3] Bool(x::Rational)
@ Base rational.jl:138
[4] Bool(x::Real)
@ Base float.jl:233
[5] (dt::Type{<:Integer})(ip::Sockets.IPAddr)
@ Sockets ~/tmp/jl/jl/julia-nightly-assert/share/julia/stdlib/v1.11/Sockets/src/IPAddr.jl:11
[6] (::Type{T})(x::Enum{T2}) where {T<:Integer, T2<:Integer}
@ Base.Enums Enums.jl:19
[7] (::Type{T})(z::Complex) where T<:Real
@ Base complex.jl:44
[8] (::Type{T})(x::Base.TwicePrecision) where T<:Number
@ Base twiceprecision.jl:265
[9] (::Type{T})(x::T) where T<:Number
@ boot.jl:894
[10] (::Type{T})(x::AbstractChar) where T<:Union{AbstractChar, Number}
@ char.jl:50
Alışılmış yapıcı sözdizimi, işlev benzeri nesne sözdizimi ile tam olarak eşdeğerdir, bu nedenle her iki sözdizimi ile bir yöntem tanımlamaya çalışmak, ilk yöntemin bir sonraki ile üzerine yazılmasına neden olacaktır:
julia> struct S
f::Int
end
julia> S() = S(7)
S
julia> (::Type{S})() = S(8) # overwrites the previous constructor method
julia> S()
S(8)
- 1Nomenclature: while the term "constructor" generally refers to the entire function which constructs objects of a type, it is common to abuse terminology slightly and refer to specific constructor methods as "constructors". In such situations, it is generally clear from the context that the term is used to mean "constructor method" rather than "constructor function", especially as it is often used in the sense of singling out a particular method of the constructor from all of the others.