Julia Language
vergelijkingen
Zoeken…
Syntaxis
- x <y # als
x
strikt kleiner is dany
- x> y # als
x
strikt groter is dany
- x == y # als
x
gelijk is aany
- x === y # of
x ≡ y
, alsx
is aany
- x ≤ y # alternatief
x <= y
, alsx
kleiner is dan of gelijk is aany
- x ≥ y # alternatief
x >= y
, alsx
groter is dan of gelijk is aany
- x ≠ y # of
x != y
, alsx
niet gelijk is aany
- x ≈ y # als
x
ongeveer gelijk is aany
Opmerkingen
Wees voorzichtig met het omdraaien van vergelijkingsborden. Julia definieert standaard veel vergelijkingsfuncties zonder de bijbehorende omgedraaide versie te definiëren. Men kan bijvoorbeeld rennen
julia> Set(1:3) ⊆ Set(0:5)
true
maar het werkt niet om te doen
julia> Set(0:5) ⊇ Set(1:3)
ERROR: UndefVarError: ⊇ not defined
Geketende vergelijkingen
Meerdere vergelijkingsoperatoren die samen worden gebruikt, zijn gekoppeld, alsof ze zijn verbonden via de operator &&
. Dit kan handig zijn voor leesbare en wiskundig beknopte vergelijkingsketens, zoals
# 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
Er is echter een belangrijk verschil tussen a > b > c
en a > b && b > c
; in het laatste geval wordt de term b
tweemaal geëvalueerd. Voor gewone oude symbolen maakt dit niet zoveel uit, maar het kan ertoe leiden dat de termen zelf bijwerkingen hebben. Bijvoorbeeld,
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
Laten we dieper gaan kijken naar geketende vergelijkingen en hoe ze werken, door te kijken hoe ze worden ontleed en in expressies worden verlaagd. Overweeg eerst de eenvoudige vergelijking, die we kunnen zien is gewoon een simpele oude functieaanroep:
julia> dump(:(a > b))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol >
2: Symbol a
3: Symbol b
typ: Any
Als we nu de vergelijking koppelen, zien we dat het parseren is gewijzigd:
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
Na het parseren wordt de uitdrukking verlaagd naar de uiteindelijke vorm:
julia> expand(:(a > b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
en we merken inderdaad op dat dit hetzelfde is als voor a > b && b >= c
:
julia> expand(:(a > b && b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
Gewone nummers
We zullen kijken hoe we aangepaste vergelijkingen kunnen implementeren door een aangepast type rangtelwoorden te implementeren. Om de implementatie te vereenvoudigen, zullen we ons concentreren op een kleine subset van deze getallen: alle rangnummers tot maar met uitzondering van ε₀. Onze implementatie is gericht op eenvoud, niet op snelheid; de implementatie is echter ook niet traag.
We slaan rangtelwoorden op in hun normale Cantor-vorm . Omdat ordinaal rekenen niet commutatief is, zullen we de gemeenschappelijke conventie nemen om eerst de belangrijkste termen op te slaan.
immutable OrdinalNumber <: Number
βs::Vector{OrdinalNumber}
cs::Vector{Int}
end
Omdat de normale vorm van Cantor uniek is, kunnen we gelijkheid eenvoudig testen door middel van recursieve gelijkheid:
In versie v0.5 is er een heel mooie syntaxis om dit compact te doen:
import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
Definieer anders de functie zoals gebruikelijker is:
import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs
Om onze bestelling te voltooien, omdat dit type een totale bestelling heeft, moeten we de isless
functie overbelasten:
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
Om onze bestelling te testen, kunnen we enkele methoden maken om rangtelwoorden te maken. Nul wordt natuurlijk verkregen door geen voorwaarden in de normale vorm van Cantor te hebben:
const ORDINAL_ZERO = OrdinalNumber([], [])
Base.zero(::Type{OrdinalNumber}) = ORDINAL_ZERO
We kunnen een expω
om ω^α
te berekenen en die gebruiken om 1 en ω te berekenen:
expω(α) = OrdinalNumber([α], [1])
const ORDINAL_ONE = expω(ORDINAL_ZERO)
Base.one(::Type{OrdinalNumber}) = ORDINAL_ONE
const ω = expω(ORDINAL_ONE)
We hebben nu een volledig functionele bestelfunctie op rangnummers:
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])
In het laatste voorbeeld zien we dat het afdrukken van rangtelwoorden beter zou kunnen zijn, maar het resultaat is zoals verwacht.
Standaard operators
Julia ondersteunt een zeer groot aantal vergelijkingsoperatoren. Waaronder
- Alle volgende unicode-reeksen:
> < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣
; - Alle symbolen in punt 1, voorafgegaan door een punt (
.
) Die elementair moeten worden gemaakt; - De operatoren
<:
,>:
,.!
, enin
, die niet kan worden voorafgegaan door een punt (.
).
Niet al deze hebben een definitie in de standaard Base
bibliotheek. Ze zijn echter beschikbaar voor andere pakketten om te definiëren en te gebruiken waar nodig.
Bij dagelijks gebruik zijn de meeste van deze vergelijkingsoperatoren niet relevant. De meest gebruikte zijn de standaard wiskundige functies voor bestellen; zie het gedeelte Syntaxis voor een lijst.
Net als de meeste andere operatoren in Julia, zijn vergelijkingsoperatoren functies die kunnen worden opgeroepen als functies. (<)(1, 2)
is bijvoorbeeld identiek in betekenis aan 1 < 2
.
Het gebruik van ==, === en isequal
Er zijn drie gelijkheidsexploitanten: ==
, ===
en isequal
. (De laatste is niet echt een operator, maar het is een functie en alle operators zijn functies.)
Wanneer ==
==
is waarde gelijkheid. Het retourneert true
wanneer twee objecten in hun huidige staat dezelfde waarde vertegenwoordigen.
Het is bijvoorbeeld duidelijk dat
julia> 1 == 1
true
maar verder
julia> 1 == 1.0
true
julia> 1 == 1.0 + 0.0im
true
julia> 1 == 1//1
true
De rechterkant van elke gelijkheid hierboven is van een ander type , maar ze vertegenwoordigen nog steeds dezelfde waarde.
Voor veranderlijke objecten, zoals arrays , vergelijkt ==
hun huidige waarde.
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
Meestal is ==
de juiste keuze.
Wanneer gebruiken ===
===
is een veel striktere operatie dan ==
. In plaats van waarde gelijkheid meet het egaliteit. Twee objecten zijn egaal als ze niet van elkaar kunnen worden onderscheiden door het programma zelf. Zo hebben we
julia> 1 === 1
true
omdat er geen manier is om een 1
onderscheiden van een andere 1
. Maar
julia> 1 === 1.0
false
omdat hoewel 1
en 1.0
dezelfde waarde hebben, ze van verschillende typen zijn en het programma ze dus uit elkaar kan houden.
Voorts
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
wat op het eerste gezicht misschien verrassend lijkt! Hoe kon het programma onderscheid maken tussen de twee vectoren A
en B
? Omdat vectoren muteerbaar zijn, zou het A
kunnen wijzigen en zou het zich anders dan B
gedragen. Maar hoe A
verandert, A
zal zich altijd hetzelfde gedragen als A
zelf. Dus A
is egaal voor A
, maar niet egaal voor B
Doorgaan volgens deze ader, observeren
julia> C = A
3-element Array{Int64,1}:
1
2
3
julia> A === C
true
Door het toewijzen van A
naar C
, zeggen we dat C
heeft aliasing A
. Dat wil zeggen, het is gewoon een andere naam voor A
. Eventuele wijzigingen aan A
worden ook door C
waargenomen. Daarom is er geen manier om het verschil tussen A
en C
te vertellen, dus ze zijn egaal.
Wanneer is isequal
Het verschil tussen ==
en isequal
is heel subtiel. Het grootste verschil zit in de manier waarop drijvende-kommagetallen worden verwerkt:
julia> NaN == NaN
false
Dit mogelijk verrassende resultaat wordt bepaald door de IEEE-standaard voor drijvende komma types (IEEE-754). Maar dit is in sommige gevallen niet handig, zoals bij sorteren. isequal
is voorzien voor die gevallen:
julia> isequal(NaN, NaN)
true
Aan de andere kant van het spectrum behandelt ==
IEEE negatieve nul en positieve nul als dezelfde waarde (ook zoals gespecificeerd door IEEE-754). Deze waarden hebben echter verschillende representaties in het geheugen.
julia> 0.0
0.0
julia> -0.0
-0.0
julia> 0.0 == -0.0
true
Wederom voor het sorteren, maakt isequal
onderscheid tussen hen.
julia> isequal(0.0, -0.0)
false