Julia Language
jämförelser
Sök…
Syntax
- x <y # om
xstrikt är mindre äny - x> y # om
xstrikt är större äny - x == y # om
xär lika medy - x === y # alternativt
x ≡ y, omxär lika medy - x ≤ y # alternativt
x <= y, omxär mindre än eller lika medy - x ≥ y # alternativt
x >= y, omxär större än eller lika medy - x ≠ y # alternativt
x != y, omxinte är lika medy - x ≈ y # om
xär ungefär lika medy
Anmärkningar
Var försiktig med att vända jämförelsetecken runt. Julia definierar många jämförelsefunktioner som standard utan att definiera motsvarande vänd version. Till exempel kan man köra
julia> Set(1:3) ⊆ Set(0:5)
true
men det fungerar inte att göra
julia> Set(0:5) ⊇ Set(1:3)
ERROR: UndefVarError: ⊇ not defined
Kedjade jämförelser
Flera jämförande operatörer som används tillsammans är kedjade, som om de är anslutna via && operatören . Detta kan vara användbart för läsbara och matematiska kortfattade jämförelsekedjor, t.ex.
# 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
Det finns emellertid en viktig skillnad mellan a > b > c och a > b && b > c ; i det senare utvärderas termen b två gånger. Detta spelar ingen roll för vanliga gamla symboler, utan kan betyda om termerna i sig har biverkningar. Till exempel,
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
Låt oss ta en djupare titt på kedjade jämförelser och hur de fungerar genom att se hur de analyseras och sänkas till uttryck . Tänk först på den enkla jämförelsen, som vi kan se är bara ett vanligt gammalt funktionssamtal:
julia> dump(:(a > b))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol >
2: Symbol a
3: Symbol b
typ: Any
Om vi nu kedjar jämförelsen, märker vi att analysen har förändrats:
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
Efter parsning sänks sedan uttrycket till sin slutliga form:
julia> expand(:(a > b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
och vi noterar verkligen att detta är detsamma som 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)
Ordningstal
Vi kommer att titta på hur man implementerar anpassade jämförelser genom att implementera en anpassad typ, ordinära nummer . För att förenkla implementeringen kommer vi att fokusera på en liten delmängd av dessa nummer: alla ordinära nummer upp till men inte inklusive ε₀. Vår implementering är inriktad på enkelhet, inte snabbhet; Implementeringen går dock inte heller långsamt.
Vi lagrar ordinära nummer enligt deras normala form . Eftersom ordinarie aritmetik inte är kommutativ kommer vi att ta den gemensamma konventionen att lagra de viktigaste termerna först.
immutable OrdinalNumber <: Number
βs::Vector{OrdinalNumber}
cs::Vector{Int}
end
Eftersom Cantors normala form är unik, kan vi testa jämställdhet helt enkelt genom rekursiv jämlikhet:
I version v0.5 finns det en mycket fin syntax för att göra detta kompakt:
import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
I annat fall definierar du funktionen som är mer typisk:
import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs
För att avsluta vår beställning, eftersom denna typ har en total order, bör vi överbelasta den isless funktionen:
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
För att testa vår ordning kan vi skapa några metoder för att skapa ordinära nummer. Noll, naturligtvis, erhålls genom att inte ha några termer i Cantor normal form:
const ORDINAL_ZERO = OrdinalNumber([], [])
Base.zero(::Type{OrdinalNumber}) = ORDINAL_ZERO
Vi kan definiera en expω att beräkna ω^α , och använda den för att beräkna 1 och ω:
expω(α) = OrdinalNumber([α], [1])
const ORDINAL_ONE = expω(ORDINAL_ZERO)
Base.one(::Type{OrdinalNumber}) = ORDINAL_ONE
const ω = expω(ORDINAL_ONE)
Vi har nu en helt funktionell beställningsfunktion på ordinära nummer:
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])
I det sista exemplet ser vi att utskriften av ordinära nummer kan vara bättre, men resultatet är som förväntat.
Standardoperatörer
Julia stöder en mycket stor uppsättning jämförelseoperatörer. Dessa inkluderar
- Alla följande unicodesekvenser:
> < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣; - Alla symboler i punkt 1 föregås av en punkt (
.) Som ska göras elementvis; - Operatörerna
<:,>:,.!, ochin, som inte kan föregås av en punkt (.).
Inte alla av dessa har en definition i standarden Base biblioteket. Men de är tillgängliga för andra paket att definiera och använda vid behov.
I vardagsbruk är de flesta av dessa jämförelser inte relevanta. De vanligaste som används är matematiska standardfunktioner för beställning; se Syntax avsnitt för en lista.
Liksom de flesta andra operatörer i Julia är jämförelsesoperatörer funktioner och kan kallas som funktioner. Till exempel är (<)(1, 2) identiska i betydelse som 1 < 2 .
Med ==, === och isequal
Det finns tre jämställdhetsoperatörer: == , === och isequal . (Den sista är egentligen inte en operatör, men det är en funktion och alla operatörer är funktioner.)
När ska man använda ==
== är värde mellan könen. Det återgår true när två objekt i sitt nuvarande tillstånd representerar samma värde.
Det är till exempel uppenbart
julia> 1 == 1
true
men dessutom
julia> 1 == 1.0
true
julia> 1 == 1.0 + 0.0im
true
julia> 1 == 1//1
true
De högra sidorna av varje jämlikhet ovan är av en annan typ , men de representerar fortfarande samma värde.
För muterbara objekt, som matriser , jämför == deras nuvarande värde.
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
För det mesta är == det rätta valet.
När ska man använda ===
=== är en mycket strängare operation än == . I stället för värdejämlikhet mäter den egaliteten. Två objekt är lika om de inte kan särskiljas från varandra av själva programmet. Således har vi
julia> 1 === 1
true
eftersom det inte finns något sätt att berätta en 1 bortsett från en annan 1 . Men
julia> 1 === 1.0
false
eftersom även om 1 och 1.0 är samma värde, är de av olika typer, och så kan programmet skilja dem från varandra.
Vidare,
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
vilket till en början verkar förvånande! Hur kunde programmet skilja mellan de två vektorerna A och B ? Eftersom vektorer är muterbara kan det modifiera A , och då skulle det bete sig annorlunda än B Men oavsett hur det modifierar A , kommer A alltid att uppträda på samma sätt som A själv. Så A är jämnt med A , men inte jämnt med B
Fortsätt längs denna ven, observera
julia> C = A
3-element Array{Int64,1}:
1
2
3
julia> A === C
true
Genom att tilldela A till C , säger vi att C har alias A Det vill säga, det har blivit ett annat namn för A Alla ändringar som görs för A kommer också att observeras av C Därför finns det inget sätt att säga skillnaden mellan A och C , så de är jämlika.
När man ska använda isequal
Skillnaden mellan == och isequal är mycket subtil. Den största skillnaden är i hur flytande punktnummer hanteras:
julia> NaN == NaN
false
Detta möjligen överraskande resultat definieras av IEEE-standarden för flytande punkttyper (IEEE-754). Men detta är inte användbart i vissa fall, till exempel sortering. isequal tillhandahålls för dessa fall:
julia> isequal(NaN, NaN)
true
På baksidan av spektrumet behandlar == IEEE negativ noll och positiv noll som samma värde (även som anges av IEEE-754). Dessa värden har emellertid distinkta representationer i minnet.
julia> 0.0
0.0
julia> -0.0
-0.0
julia> 0.0 == -0.0
true
Återigen för sorteringsändamål skiljer isequal mellan dem.
julia> isequal(0.0, -0.0)
false