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.IsInfinite
undBase.SizeUnknown
sind 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
sin
beispielsweise 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)