Julia Language
Typen
Suche…
Syntax
- unveränderlich MyType; Feld; Feld; Ende
- Geben Sie MyType ein. Feld; Feld; Ende
Bemerkungen
Typen sind der Schlüssel zu Julias Leistung. Eine wichtige Idee für die Leistung ist die Typstabilität , die auftritt, wenn der von einer Funktion zurückgegebene Typ nur von den Typen und nicht von den Werten ihrer Argumente abhängt.
Versand auf Typen
In Julia können Sie für jede Funktion mehrere Methoden definieren. Nehmen wir an, wir definieren drei Methoden derselben Funktion:
foo(x) = 1
foo(x::Number) = 2
foo(x::Int) = 3
Bei der Entscheidung, welche Methode verwendet werden soll (als " dispatch" bezeichnet ), wählt Julia die spezifischere Methode aus, die den Typen der Argumente entspricht:
julia> foo('one')
1
julia> foo(1.0)
2
julia> foo(1)
3
Dies erleichtert den Polymorphismus . Beispielsweise können Sie leicht eine verknüpfte Liste erstellen, indem Sie zwei unveränderliche Typen definieren, die als Nil und Cons . Diese Namen werden traditionell verwendet, um eine leere Liste bzw. eine nicht leere Liste zu beschreiben.
abstract LinkedList
immutable Nil <: LinkedList end
immutable Cons <: LinkedList
first
rest::LinkedList
end
Die leere Liste wird von Nil() und alle anderen Listen von Cons(first, rest) , wobei first das erste Element der verknüpften Liste und rest die verknüpfte Liste ist, die aus allen übrigen Elementen besteht. Beispielsweise wird die Liste [1, 2, 3] als dargestellt
julia> Cons(1, Cons(2, Cons(3, Nil())))
Cons(1,Cons(2,Cons(3,Nil())))
Ist die Liste leer?
Angenommen, wir möchten die isempty Funktion der Standardbibliothek isempty , die für verschiedene Sammlungen verwendet wird:
julia> methods(isempty)
# 29 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
...
Wir können einfach die Funktionssendungssyntax verwenden und zwei zusätzliche Methoden für isempty . Da diese Funktion von dem ist Base haben wir es als qualifizieren Base.isempty , um es zu erweitern.
Base.isempty(::Nil) = true
Base.isempty(::Cons) = false
Hier haben wir die Argumentwerte überhaupt nicht benötigt, um festzustellen, ob die Liste leer ist. Nur der Typ allein reicht aus, um diese Informationen zu berechnen. Julia erlaubt uns, die Namen der Argumente wegzulassen und nur die Typanmerkung beizubehalten, wenn wir ihre Werte nicht verwenden müssen.
Wir können testen, ob unsere isempty Methoden funktionieren:
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())))))
und in der Tat hat sich die Anzahl der Methoden für isempty um 2 erhöht:
julia> methods(isempty)
# 31 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
Die Feststellung, ob eine verknüpfte Liste leer ist oder nicht, ist eindeutig ein triviales Beispiel. Aber es führt zu etwas Interessanterem:
Wie lang ist die Liste?
Die length aus der Standardbibliothek gibt die Länge einer Sammlung oder bestimmte iterierbare Werte an . Es gibt viele Möglichkeiten, um length für eine verknüpfte Liste zu implementieren. Insbesondere ist die Verwendung einer while Schleife in Julia wahrscheinlich am schnellsten und am meisten Speichereffizienz. Eine vorzeitige Optimierung sollte jedoch vermieden werden. Nehmen wir einmal an, dass unsere verknüpfte Liste nicht effizient sein muss. Was ist der einfachste Weg , um einen schreiben length Funktion?
Base.length(::Nil) = 0
Base.length(xs::Cons) = 1 + length(xs.rest)
Die erste Definition ist einfach: Eine leere Liste hat die Länge 0 . Die zweite Definition ist auch leicht zu lesen: Um die Länge einer Liste zu zählen, zählen wir das erste Element und dann die Länge des Restes der Liste. Wir können diese Methode ähnlich testen, wie wir es getestet 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ächste Schritte
Dieses Spielzeugbeispiel ist ziemlich weit davon entfernt, alle Funktionen zu implementieren, die in einer verknüpften Liste erwünscht wären. Es fehlt beispielsweise die Iterationsschnittstelle. Es zeigt jedoch, wie mit dem Versand kurzer und klarer Code geschrieben werden kann.
Unveränderliche Typen
Der einfachste zusammengesetzte Typ ist ein unveränderlicher Typ. Instanzen unveränderlicher Typen wie Tupel sind Werte. Ihre Felder können nicht geändert werden, nachdem sie erstellt wurden. In vielerlei Hinsicht ähnelt ein unveränderlicher Typ einem Tuple mit Namen für den Typ selbst und für jedes Feld.
Singleton-Typen
Zusammengesetzte Typen enthalten per Definition eine Anzahl einfacherer Typen. In Julia kann diese Zahl Null sein. Das heißt, ein unveränderlicher Typ darf keine Felder enthalten. Dies ist vergleichbar mit dem leeren Tupel () .
Warum kann das nützlich sein? Solche unveränderlichen Typen werden als "Singleton-Typen" bezeichnet, da nur eine Instanz davon existieren könnte. Die Werte solcher Typen werden als "Singleton-Werte" bezeichnet. Die Standardbibliothek Base enthält viele solcher Singleton-Typen. Hier ist eine kurze Liste:
-
Void, die Art vonnothing. Wir können überprüfen, dassVoid.instance(die spezielle Syntax zum Abrufen des Singleton-Werts eines Singleton-Typs) tatsächlichnothing. - Jeder Medientyp wie
MIME"text/plain"ist ein Singleton-Typ mit einer einzigen Instanz,MIME("text/plain"). -
Irrational{:π},Irrational{:e},Irrational{:φ}und ähnliche Typen sind Singleton-Typen, und ihre Singleton-Instanzen sind die irrationalen Werteπ = 3.1415926535897...usw. - Die Iteratorgrößenmerkmale
Base.HasLength,Base.HasShape,Base.IsInfiniteundBase.SizeUnknownsind alle Singleton-Typen.
- In Version 0.5 und höher ist jede Funktion eine Singleton-Instanz eines Singleton-Typs! Wie jeder andere Einzelwert können wir die Funktion
sinbeispielsweise austypeof(sin).instance.
Da sie nichts enthalten, sind Singleton-Typen unglaublich leicht und können vom Compiler häufig dahin gehend optimiert werden, dass sie keinen Laufzeit-Overhead haben. Daher sind sie perfekt für Merkmale, spezielle Tag-Werte und für Funktionen wie Funktionen, auf die Sie sich spezialisieren möchten.
Um einen Singleton-Typ zu definieren,
julia> immutable MySingleton end
So definieren Sie einen benutzerdefinierten Druck für den Singleton-Typ
julia> Base.show(io::IO, ::MySingleton) = print(io, "sing")
Um auf die Singleton-Instanz zuzugreifen,
julia> MySingleton.instance
MySingleton()
Oft ordnet man dies einer Konstanten zu:
julia> const sing = MySingleton.instance
MySingleton()
Wrapper-Typen
Wenn unveränderliche Nullfeldtypen interessant und nützlich sind, sind möglicherweise unveränderliche Einfeldtypen möglicherweise noch nützlicher. Solche Typen werden im Allgemeinen als "Wrapper-Typen" bezeichnet, da sie einige zugrunde liegende Daten umschließen, wodurch eine alternative Schnittstelle zu den Daten bereitgestellt wird. Ein Beispiel für einen Wrapper-Typ in Base ist String . Wir definieren einen ähnlichen String MyString . Dieser Typ wird durch einen Vektor (eindimensionales Array ) von Bytes ( UInt8 ) UInt8 .
Zuerst die Typdefinition selbst und einige angepasste Anzeigen:
immutable MyString <: AbstractString
data::Vector{UInt8}
end
function Base.show(io::IO, s::MyString)
print(io, "MyString: ")
write(io, s.data)
return
end
Jetzt ist unser MyString Typ einsatzbereit! Wir können ihm einige unreife UTF-8-Daten zuführen, und es zeigt an, wie es uns gefällt:
julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!
Offensichtlich erfordert dieser String-Typ eine Menge Arbeit, bevor er so nutzbar ist wie der Base.String Typ.
Echte zusammengesetzte Typen
Am häufigsten enthalten viele unveränderliche Typen mehr als ein Feld. Ein Beispiel ist die Standardbibliothek Rational{T} , die zwei Felder enthält: ein num Feld für den Zähler und ein den Feld für den Nenner. Es ist ziemlich einfach, diesen Typ zu emulieren:
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)
Wir haben erfolgreich einen Konstruktor implementiert, der unsere rationalen Zahlen vereinfacht:
julia> MyRational(10, 6)
MyRational{Int64}(5,3)