Zoeken…


Syntaxis

  • onveranderlijk MyType; veld; veld; einde
  • typ MyType; veld; veld; einde

Opmerkingen

Typen zijn de sleutel tot Julia's uitvoering. Een belangrijk idee voor prestaties is type stabiliteit , die optreedt wanneer het type dat een functie retourneert alleen afhankelijk is van de typen, niet de waarden, van de argumenten.

Verzenden op typen

Op Julia kunt u meer dan één methode voor elke functie definiëren. Stel dat we drie methoden met dezelfde functie definiëren:

foo(x) = 1
foo(x::Number) = 2
foo(x::Int) = 3

Bij het beslissen welke methode ze moet gebruiken ( verzending genoemd ), kiest Julia de meer specifieke methode die overeenkomt met de typen argumenten:

julia> foo('one')
1

julia> foo(1.0)
2

julia> foo(1)
3

Dit vergemakkelijkt polymorfisme . We kunnen bijvoorbeeld eenvoudig een gekoppelde lijst maken door twee onveranderlijke typen te definiëren, genaamd Nil en Cons . Deze namen worden traditioneel gebruikt om respectievelijk een lege lijst en een niet-lege lijst te beschrijven.

abstract LinkedList
immutable Nil <: LinkedList end
immutable Cons <: LinkedList
    first
    rest::LinkedList
end

We zullen de lege lijst weergeven door Nil() en alle andere lijsten door Cons(first, rest) , waarbij first het eerste element van de gekoppelde lijst is en rest de gekoppelde lijst die bestaat uit alle resterende elementen. De lijst [1, 2, 3] wordt bijvoorbeeld weergegeven als

julia> Cons(1, Cons(2, Cons(3, Nil())))
Cons(1,Cons(2,Cons(3,Nil())))

Is de lijst leeg?

Stel dat we de isempty functie van de standaardbibliotheek willen uitbreiden, die op verschillende collecties werkt:

julia> methods(isempty)
# 29 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
...

We kunnen eenvoudig de syntaxis van de functie verzending gebruiken en twee extra methoden van isempty . Aangezien deze functie afkomstig is van de Base , moeten we deze kwalificeren als Base.isempty om deze uit te breiden.

Base.isempty(::Nil) = true
Base.isempty(::Cons) = false

Hier hadden we de argumentwaarden helemaal niet nodig om te bepalen of de lijst leeg is. Alleen het type alleen is voldoende om die informatie te berekenen. Julia staat ons toe de namen van argumenten weg te laten en alleen hun type-annotatie te behouden als we hun waarden niet hoeven te gebruiken.

We kunnen testen dat onze isempty methoden werken:

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())))))

en inderdaad is het aantal methoden voor isempty met 2 toegenomen:

julia> methods(isempty)
# 31 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394

Het is duidelijk dat het een triviaal voorbeeld is om te bepalen of een gekoppelde lijst leeg is of niet. Maar het leidt tot iets interessants:

Hoe lang is de lijst?

De length functie van de standaard bibliotheek geeft ons de lengte van een verzameling of bepaalde iterables . Er zijn veel manieren om length te implementeren voor een gekoppelde lijst. Met name het gebruik van een while lus is waarschijnlijk het snelst en het meest geheugenefficiënt in Julia. Maar voortijdige optimalisatie moet worden vermeden, dus laten we even veronderstellen dat onze gekoppelde lijst niet efficiënt hoeft te zijn. Wat is de eenvoudigste manier om een schrijven length functie?

Base.length(::Nil) = 0
Base.length(xs::Cons) = 1 + length(xs.rest)

De eerste definitie is eenvoudig: een lege lijst heeft lengte 0 . De tweede definitie is ook gemakkelijk te lezen: om de lengte van een lijst te tellen, tellen we het eerste element en tellen we vervolgens de lengte van de rest van de lijst. We kunnen deze methode op dezelfde manier testen als hoe we isempty hebben getest:

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

Volgende stappen

Dit speelgoedvoorbeeld is vrij verre van het implementeren van alle functionaliteit die gewenst zou zijn in een gekoppelde lijst. Het ontbreekt bijvoorbeeld aan de iteratie-interface. Het illustreert echter hoe verzending kan worden gebruikt om korte en duidelijke code te schrijven.

Onveranderlijke typen

Het eenvoudigste composiettype is een onveranderlijk type. Gevallen van onveranderlijke typen, zoals tupels , zijn waarden. Hun velden kunnen niet worden gewijzigd nadat ze zijn gemaakt. In veel opzichten is een onveranderlijk type als een Tuple met namen voor het type zelf en voor elk veld.

Singleton-typen

Samengestelde typen bevatten per definitie een aantal eenvoudigere typen. In Julia kan dit getal nul zijn; dat wil zeggen, een onveranderlijk type mag geen velden bevatten. Dit is vergelijkbaar met de lege tuple () .

Waarom kan dit nuttig zijn? Dergelijke onveranderlijke typen staan bekend als 'singleton-typen', aangezien er slechts één exemplaar van zou kunnen bestaan. De waarden van dergelijke typen staan bekend als "singleton-waarden". De standaard bibliotheek Base bevat veel van dergelijke Singleton types. Hier is een korte lijst:

  • Void , het soort nothing . We kunnen verifiëren dat Void.instance (wat een speciale syntaxis is voor het ophalen van de singleton-waarde van een singleton-type) inderdaad nothing .
  • Elk mediatype, zoals MIME"text/plain" , is een singleton-type met een enkele instantie, MIME("text/plain") .
  • De Irrational{:π} , Irrational{:e} , Irrational{:φ} en vergelijkbare typen zijn singleton-typen en hun singleton-instanties zijn de irrationele waarden π = 3.1415926535897... , enz.
  • De iterator-maat Base.HasLength , Base.HasShape , Base.IsInfinite en Base.SizeUnknown zijn allemaal singleton-typen.
0.5.0
  • In versie 0.5 en hoger is elke functie een singletoninstantie van het type singleton! Net als elke andere singleton-waarde kunnen we de functie sin bijvoorbeeld herstellen van typeof(sin).instance .

Omdat ze niets bevatten, zijn singleton-typen ongelooflijk licht en kunnen ze vaak door de compiler worden geoptimaliseerd om geen runtime overhead te hebben. Ze zijn dus perfect voor eigenschappen, speciale tagwaarden en voor dingen zoals functies waarop men zich zou willen specialiseren.

Om een singleton-type te definiëren,

julia> immutable MySingleton end

Om aangepast afdrukken voor het type singleton te definiëren,

julia> Base.show(io::IO, ::MySingleton) = print(io, "sing")

Om toegang te krijgen tot de singleton-instantie,

julia> MySingleton.instance
MySingleton()

Vaak wijst men dit toe aan een constante:

julia> const sing = MySingleton.instance
MySingleton()

Wrapper types

Als onveranderlijke typen met nul velden interessant en nuttig zijn, zijn wellicht onveranderlijke typen met één veld nog nuttiger. Dergelijke typen worden gewoonlijk "omhullingsoorten" genoemd omdat ze sommige onderliggende gegevens omwikkelen, waardoor een alternatieve interface voor genoemde gegevens wordt verschaft. Een voorbeeld van een wrapper-type in Base is String . We zullen een soortgelijk type definiëren als String , genaamd MyString . Dit type wordt ondersteund door een vector (eendimensionale array ) bytes ( UInt8 ).

Eerst de typedefinitie zelf en een aantal aangepaste weergaven:

immutable MyString <: AbstractString
    data::Vector{UInt8}
end

function Base.show(io::IO, s::MyString)
    print(io, "MyString: ")
    write(io, s.data)
    return
end

Nu is ons MyString type klaar voor gebruik! We kunnen het wat onbewerkte UTF-8-gegevens geven en het wordt weergegeven zoals we willen:

julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!

Het is duidelijk dat dit Base.String veel werk vereist voordat het net zo bruikbaar wordt als het Base.String type.

Echte composiettypen

Misschien zijn de meeste onveranderlijke typen meer dan één veld. Een voorbeeld is het standaardbibliotheektype Rational{T} , dat twee velden bevat: een num veld voor de teller en een den veld voor de noemer. Het is vrij eenvoudig om dit typeontwerp te emuleren:

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)

We hebben met succes een constructor geïmplementeerd die onze rationale getallen vereenvoudigt:

julia> MyRational(10, 6)
MyRational{Int64}(5,3)


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow