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 av nothing . Vi kan verifiera att Void.instance (som är en speciell syntax för att hämta singletonvärdet för en singleton-typ) inte är nothing .
  • 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 och Base.SizeUnknown är alla typer av singleton.
0.5.0
  • 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ån typeof(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)


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow