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 von nothing . Wir können überprüfen, dass Void.instance (die spezielle Syntax zum Abrufen des Singleton-Werts eines Singleton-Typs) tatsächlich nothing .
  • 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 und Base.SizeUnknown sind alle Singleton-Typen.
0,5,0
  • 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 aus typeof(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)


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow