Julia Language
jämförelser
Sök…
Syntax
- x <y # om
x
strikt är mindre äny
- x> y # om
x
strikt ä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
, omx
inte ä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