Sök…


Syntax

  • x <y # om x strikt är mindre än y
  • x> y # om x strikt är större än y
  • x == y # om x är lika med y
  • x === y # alternativt x ≡ y , om x är lika med y
  • x ≤ y # alternativt x <= y , om x är mindre än eller lika med y
  • x ≥ y # alternativt x >= y , om x är större än eller lika med y
  • x ≠ y # alternativt x != y , om x inte är lika med y
  • x ≈ y # om x är ungefär lika med y

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:

0.5.0

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
0.5.0

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

  1. Alla följande unicodesekvenser: > < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣ ;
  2. Alla symboler i punkt 1 föregås av en punkt ( . ) Som ska göras elementvis;
  3. Operatörerna <: , >: , .! , och in , 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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow