Julia Language
typer
Sök…
Syntax
- immutable MyType; fält; fält; slutet
- skriv MyType; fält; fält; slutet
Anmärkningar
Typer är nyckeln till Julias prestanda. En viktig idé för prestanda är typstabilitet , som uppstår när den typ som en funktion returnerar endast beror på typerna, inte värdena, för dess argument.
Skickar på typer
På Julia kan du definiera mer än en metod för varje funktion. Anta att vi definierar tre metoder för samma funktion:
foo(x) = 1
foo(x::Number) = 2
foo(x::Int) = 3
När hon bestämmer vilken metod som ska användas (kallas avsändning ) väljer Julia den mer specifika metoden som matchar argumenttyperna:
julia> foo('one')
1
julia> foo(1.0)
2
julia> foo(1)
3
Detta underlättar polymorfism . Vi kan till exempel enkelt skapa en länkad lista genom att definiera två immutable-typer, med namnet Nil
och Cons
. Dessa namn används traditionellt för att beskriva en tom lista respektive en icke-tom lista.
abstract LinkedList
immutable Nil <: LinkedList end
immutable Cons <: LinkedList
first
rest::LinkedList
end
Vi kommer att representera den tomma listan av Nil()
och alla andra listor av Cons(first, rest)
, där det first
är det första elementet i den länkade listan och rest
är den länkade listan som består av alla återstående element. Till exempel kommer listan [1, 2, 3]
att representeras som
julia> Cons(1, Cons(2, Cons(3, Nil())))
Cons(1,Cons(2,Cons(3,Nil())))
Är listan tom?
Anta att vi vill utöka standardbibliotekets isempty
, som fungerar på en mängd olika samlingar:
julia> methods(isempty)
# 29 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
...
Vi kan helt enkelt använda syntaxen för funktionens avsändning och definiera ytterligare två metoder för isempty
. Eftersom denna funktion är från Base
modulen, måste vi kvalificera den som Base.isempty
för att förlänga den.
Base.isempty(::Nil) = true
Base.isempty(::Cons) = false
Här behövde vi inte argumentvärden alls för att avgöra om listan är tom. Bara typen räcker för att beräkna den informationen. Julia tillåter oss att utelämna namnen på argument och bara behålla deras typanteckningar om vi inte behöver använda deras värden.
Vi kan testa att våra isempty
fungerar:
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())))))
och antalet metoder för isempty
har isempty
ökat med 2
:
julia> methods(isempty)
# 31 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
Det är tydligt att bestämma om en länkad lista är tom eller inte är ett triviellt exempel. Men det leder till något mer intressant:
Hur lång är listan?
Den length
funktion från standardbiblioteket ger oss längden av en samling eller vissa iterables . Det finns många sätt att implementera length
för en länkad lista. I synnerhet, med en while
slinga är sannolikt snabbaste och mest minneseffektiva i Julia. Men för tidig optimering är att undvika, så låt oss anta en sekund att vår länkade lista inte behöver vara effektiv. Vad är det enklaste sättet att skriva en length
funktion?
Base.length(::Nil) = 0
Base.length(xs::Cons) = 1 + length(xs.rest)
Den första definitionen är enkel: en tom lista har längd 0
. Den andra definitionen är också lättläst: att räkna längden på en lista räknar vi det första elementet och räknar sedan längden på resten av listan. Vi kan testa den här metoden på samma sätt som vi testade 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
Nästa steg
Detta leksaksexempel är ganska långt ifrån att implementera all funktionalitet som skulle önskas i en länkad lista. Det saknas till exempel iterationsgränssnittet. Det illustrerar dock hur avsändning kan användas för att skriva kort och tydlig kod.
Oändliga typer
Den enklaste sammansatta typen är en oföränderlig typ. Förekomsten av oföränderliga typer, som tuple , är värden. Deras fält kan inte ändras efter att de har skapats. På många sätt är en immutable typ som en Tuple
med namn på själva typen och för varje fält.
Singleton typer
Komposittyper innehåller per definition ett antal enklare typer. I Julia kan detta antal vara noll; det vill säga att en oföränderlig typ får inte innehålla några fält. Detta kan jämföras med den tomma tupeln ()
.
Varför kan detta vara användbart? Sådana oföränderliga typer är kända som "singletontyper", eftersom endast en instans av dem någonsin skulle kunna existera. Värdena för sådana typer kallas "singleton-värden". Standardbiblioteket Base
innehåller många sådana singleton typer. Här är en kort lista:
-
Void
, typen avnothing
. Vi kan verifiera attVoid.instance
(som är en speciell syntax för att hämta singletonvärdet för en singleton-typ) inte ärnothing
. - Alla typer av medier, till exempel
MIME"text/plain"
, är en singleton-typ med en enda instans,MIME("text/plain")
. -
Irrational{:π}
,Irrational{:e}
,Irrational{:φ}
och liknande typer är singletontyper, och deras singletoninstanser är de irrationella värdenaπ = 3.1415926535897...
, etc. - Storleken på iteratorstorleken
Base.HasLength
,Base.HasShape
,Base.IsInfinite
ochBase.SizeUnknown
är alla typer av singleton.
- I version 0.5 och senare är varje funktion en singleton-instans av en singleton-typ! Som alla andra singletonvärden kan vi återställa funktionen
sin
, till exempel fråntypeof(sin).instance
.
Eftersom de inte innehåller något, är singleton-typer oerhört lätta och de kan ofta optimeras bort av kompilatorn så att de inte har några driftskostnader. Således är de perfekta för drag, specialtaggvärden och för saker som funktioner som man vill specialisera sig på.
För att definiera en singletontyp,
julia> immutable MySingleton end
För att definiera anpassad utskrift för singleton-typen,
julia> Base.show(io::IO, ::MySingleton) = print(io, "sing")
För att komma åt singleton-instansen,
julia> MySingleton.instance
MySingleton()
Ofta tilldelar man detta till en konstant:
julia> const sing = MySingleton.instance
MySingleton()
Omslagstyper
Om obearterbara typer med nollfält är intressanta och användbara är kanske enfältets immutabla typer ännu mer användbara. Sådana typer kallas vanligtvis "omslagstyper" eftersom de inslagar vissa underliggande data, vilket ger ett alternativt gränssnitt till nämnda data. Ett exempel på en omslagstyp i Base
är String
. Vi kommer att definiera en liknande typ som String
, med namnet MyString
. Denna typ stöds av en vektor (endimensionell matris ) av byte ( UInt8
).
Först själva typdefinitionen och vissa anpassade visar:
immutable MyString <: AbstractString
data::Vector{UInt8}
end
function Base.show(io::IO, s::MyString)
print(io, "MyString: ")
write(io, s.data)
return
end
Nu är vår MyString
typ klar för användning! Vi kan mata lite rå UTF-8-data, och de visas som vi vill:
julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!
Uppenbarligen behöver den här Base.String
mycket arbete innan den blir lika användbar som typen Base.String
.
Äkta sammansatta typer
Kanske oftast innehåller många oföränderliga typer mer än ett fält. Ett exempel är den standardbiblioteket Rational{T}
typ, som innehåller två fieds: en num
fält för täljaren, och en den
fält för nämnaren. Det är ganska enkelt att efterlikna denna typ:
immutable MyRational{T}
num::T
den::T
MyRational(n, d) = (g = gcd(n, d); new(n÷g, d÷g))
end
MyRational{T}(n::T, d::T) = MyRational{T}(n, d)
Vi har framgångsrikt implementerat en konstruktör som förenklar våra rationella antal:
julia> MyRational(10, 6)
MyRational{Int64}(5,3)