Julia Language
Vergleiche
Suche…
Syntax
- x <y #, wenn
x
streng weniger alsy
- x> y # wenn
x
streng größer alsy
- x == y # wenn
x
gleichy
- x === y # alternativ
x ≡ y
, wennx
füry
egal ist - x ≤ y # alternativ
x <= y
, wennx
kleiner oder gleichy
- x ≥ y # alternativ
x >= y
, wennx
größer oder gleichy
- x ≠ y # alternativ
x != y
, wennx
nicht gleichy
- x ≈ y #, wenn
x
ungefähr gleichy
Bemerkungen
Achten Sie darauf, die Vergleichszeichen umzudrehen. Julia definiert standardmäßig viele Vergleichsfunktionen, ohne die entsprechende umgedrehte Version zu definieren. Zum Beispiel kann man laufen
julia> Set(1:3) ⊆ Set(0:5)
true
aber es funktioniert nicht
julia> Set(0:5) ⊇ Set(1:3)
ERROR: UndefVarError: ⊇ not defined
Verkettete Vergleiche
Mehrere miteinander kombinierte Vergleichsoperatoren werden wie über den Operator &&
verbunden miteinander verkettet. Dies kann nützlich sein für lesbare und mathematisch präzise Vergleichsketten, wie z
# same as 0 < i && i <= length(A)
isinbounds(A, i) = 0 < i ≤ length(A)
# same as Set() != x && issubset(x, y)
isnonemptysubset(x, y) = Set() ≠ x ⊆ y
Es gibt jedoch einen wichtigen Unterschied zwischen a > b > c
und a > b && b > c
; in letzterer wird der Begriff b
zweimal bewertet. Dies spielt für einfache alte Symbole keine Rolle, könnte jedoch von Bedeutung sein, wenn die Begriffe selbst Nebenwirkungen haben. Zum Beispiel,
julia> f(x) = (println(x); 2)
f (generic function with 1 method)
julia> 3 > f("test") > 1
test
true
julia> 3 > f("test") && f("test") > 1
test
test
true
Schauen wir uns die verketteten Vergleiche und ihre Funktionsweise genauer an, indem wir sehen, wie sie analysiert und in Ausdrücke abgesenkt werden. Betrachten wir zunächst den einfachen Vergleich, den wir als einfachen alten Funktionsaufruf sehen:
julia> dump(:(a > b))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol >
2: Symbol a
3: Symbol b
typ: Any
Wenn wir nun den Vergleich verketten, stellen wir fest, dass sich das Parsing geändert hat:
julia> dump(:(a > b >= c))
Expr
head: Symbol comparison
args: Array{Any}((5,))
1: Symbol a
2: Symbol >
3: Symbol b
4: Symbol >=
5: Symbol c
typ: Any
Nach dem Parsen wird der Ausdruck in seine endgültige Form abgesenkt:
julia> expand(:(a > b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
und wir stellen fest, dass dies dasselbe ist wie für a > b && b >= c
:
julia> expand(:(a > b && b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
Ordnungszahlen
Wir werden untersuchen, wie Sie benutzerdefinierte Vergleiche implementieren, indem Sie einen benutzerdefinierten Typ Ordnungszahlen implementieren. Um die Implementierung zu vereinfachen, konzentrieren wir uns auf eine kleine Teilmenge dieser Zahlen: alle Ordnungszahlen bis, jedoch nicht einschließlich ε₀. Unsere Implementierung konzentriert sich auf Einfachheit und nicht auf Geschwindigkeit. Die Implementierung ist jedoch auch nicht langsam.
Wir speichern Ordinalzahlen in ihrer normalen Cantor-Form . Da die ordinale Arithmetik nicht kommutativ ist, verwenden wir die übliche Konvention, zuerst die wichtigsten Bezeichnungen zu speichern.
immutable OrdinalNumber <: Number
βs::Vector{OrdinalNumber}
cs::Vector{Int}
end
Da die Cantor-Normalform einzigartig ist, können wir Gleichheit einfach durch rekursive Gleichheit testen:
In Version v0.5 gibt es eine sehr schöne Syntax, um dies kompakt zu machen:
import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
Andernfalls definieren Sie die Funktion wie üblich:
import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs
Um unsere Bestellung isless
, sollten Sie die isless
Funktion überladen, da dieser Typ eine Gesamtbestellung hat.
import Base: isless
function isless(α::OrdinalNumber, β::OrdinalNumber)
for i in 1:min(length(α.cs), length(β.cs))
if α.βs[i] < β.βs[i]
return true
elseif α.βs[i] == β.βs[i] && α.cs[i] < β.cs[i]
return true
end
end
return length(α.cs) < length(β.cs)
end
Um unsere Reihenfolge zu testen, können wir einige Methoden erstellen, um Ordnungszahlen zu erstellen. Null wird natürlich dadurch erhalten, dass keine Terme in der Cantor-Normalform vorliegen:
const ORDINAL_ZERO = OrdinalNumber([], [])
Base.zero(::Type{OrdinalNumber}) = ORDINAL_ZERO
Wir können ein expω
definieren, um ω^α
zu berechnen, und daraus 1 und ω berechnen:
expω(α) = OrdinalNumber([α], [1])
const ORDINAL_ONE = expω(ORDINAL_ZERO)
Base.one(::Type{OrdinalNumber}) = ORDINAL_ONE
const ω = expω(ORDINAL_ONE)
Wir haben jetzt eine voll funktionsfähige Ordnungsfunktion für Ordnungszahlen:
julia> ORDINAL_ZERO < ORDINAL_ONE < ω < expω(ω)
true
julia> ORDINAL_ONE > ORDINAL_ZERO
true
julia> sort([ORDINAL_ONE, ω, expω(ω), ORDINAL_ZERO])
4-element Array{OrdinalNumber,1}:
OrdinalNumber(OrdinalNumber[],Int64[])
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],[1])
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],[1])],[1])
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],[1])],[1])],[1])
Im letzten Beispiel sehen wir, dass das Drucken von Ordnungszahlen besser sein könnte, aber das Ergebnis ist wie erwartet.
Standardoperatoren
Julia unterstützt sehr viele Vergleichsoperatoren. Diese schließen ein
- Alle folgenden Unicode-Sequenzen:
> < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣
- Alle Symbole in Punkt 1, vorangestellt von einem Punkt (
.
), Der elementweise erstellt werden soll; - Die Operatoren
<:
,>:
,.!
undin
, dem kein Punkt (.
) vorangestellt werden kann.
Nicht alle haben eine Definition in der Standard- Base
Bibliothek. Sie sind jedoch für andere Pakete verfügbar, um sie entsprechend zu definieren und zu verwenden.
Im täglichen Gebrauch sind die meisten dieser Vergleichsoperatoren nicht relevant. Die am häufigsten verwendeten sind die mathematischen Standardfunktionen für die Reihenfolge; Eine Liste finden Sie im Abschnitt Syntax.
Wie die meisten anderen Operatoren in Julia sind Vergleichsoperatoren Funktionen und können als Funktionen aufgerufen werden. Zum Beispiel ist (<)(1, 2)
in der Bedeutung mit 1 < 2
identisch.
Verwenden Sie ==, === und ist gleich
Es gibt drei Gleichheitsoperatoren: ==
, ===
und isequal
. (Der letzte ist nicht wirklich ein Operator, aber es ist eine Funktion und alle Operatoren sind Funktionen.)
Wann verwenden ==
==
ist Wertgleichheit. Sie gibt true
zurück true
wenn zwei Objekte in ihrem aktuellen Zustand denselben Wert darstellen.
Zum Beispiel ist es offensichtlich, dass
julia> 1 == 1
true
aber darüber hinaus
julia> 1 == 1.0
true
julia> 1 == 1.0 + 0.0im
true
julia> 1 == 1//1
true
Die rechte Seite jeder obigen Gleichheit ist von einem anderen Typ , sie repräsentieren jedoch immer noch denselben Wert.
Bei veränderlichen Objekten wie Arrays vergleicht ==
ihren aktuellen Wert.
julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> B = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> C = [1, 3, 2]
3-element Array{Int64,1}:
1
3
2
julia> A == B
true
julia> A == C
false
julia> A[2], A[3] = A[3], A[2] # swap 2nd and 3rd elements of A
(3,2)
julia> A
3-element Array{Int64,1}:
1
3
2
julia> A == B
false
julia> A == C
true
Meistens ist ==
die richtige Wahl.
Wann verwenden ===
===
ist eine weitaus strengere Operation als ==
. Anstelle von Wertgleichheit misst es die Egalität. Zwei Objekte sind egal, wenn sie vom Programm selbst nicht unterschieden werden können. So haben wir
julia> 1 === 1
true
da es keine Möglichkeit gibt, eine 1
von einer anderen 1
. Aber
julia> 1 === 1.0
false
Obwohl 1
und 1.0
den gleichen Wert haben, unterscheiden sie sich, und das Programm kann sie unterscheiden.
Außerdem,
julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> B = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> A === B
false
julia> A === A
true
was auf den ersten Blick überraschend erscheinen mag! Wie konnte das Programm zwischen den beiden Vektoren A
und B
? Da Vektoren veränderlich sind, können sie A
modifizieren und verhalten sich dann anders als B
Unabhängig davon, wie A
geändert wird, verhält sich A
immer wie A
selbst. Also ist A
egal zu A
, aber nicht egal zu B
Weiter entlang dieser Ader beobachten
julia> C = A
3-element Array{Int64,1}:
1
2
3
julia> A === C
true
Durch die Zuordnung von A
bis C
, sagen wir , dass C
aliased hat A
. Das heißt, es ist nur ein anderer Name für A
. Alle Änderungen an A
werden von C
ebenfalls beachtet. Daher gibt es keine Möglichkeit, den Unterschied zwischen A
und C
erkennen. Sie sind also egal.
Wann verwendet man isequal
Der Unterschied zwischen ==
und isequal
ist sehr subtil. Der größte Unterschied besteht im Umgang mit Gleitkommazahlen:
julia> NaN == NaN
false
Dies möglicherweise raschendes Ergebnis ist definiert durch den IEEE - Standard für Gleitkomma - Typen (IEEE-754). Dies ist jedoch in einigen Fällen nicht sinnvoll, beispielsweise beim Sortieren. isequal
wird für diese Fälle bereitgestellt:
julia> isequal(NaN, NaN)
true
Auf der anderen Seite des Spektrums behandelt ==
IEEE-negative Null und positive Null als denselben Wert (auch wie von IEEE-754 angegeben). Diese Werte haben jedoch unterschiedliche Repräsentationen im Speicher.
julia> 0.0
0.0
julia> -0.0
-0.0
julia> 0.0 == -0.0
true
Auch isequal
unterscheidet sie sich zu Sortierzwecken.
julia> isequal(0.0, -0.0)
false