Szukaj…


Składnia

  • x <y # jeśli x jest ściśle mniejsze niż y
  • x> y # jeśli x jest ściśle większe niż y
  • x == y # jeśli x jest równe y
  • x === y # alternatywnie x ≡ y , jeśli x jest równe y
  • x ≤ y # alternatywnie x <= y , jeśli x jest mniejsze lub równe y
  • x ≥ y # alternatywnie x >= y , jeśli x jest większe lub równe y
  • x ≠ y # alternatywnie x != y , jeśli x nie jest równe y
  • x ≈ y # jeśli x jest w przybliżeniu równe y

Uwagi

Uważaj na odwracanie znaków porównawczych. Julia domyślnie definiuje wiele funkcji porównawczych bez definiowania odpowiedniej odwróconej wersji. Na przykład można uruchomić

julia> Set(1:3) ⊆ Set(0:5)
true

ale to nie działa

julia> Set(0:5) ⊇ Set(1:3)
ERROR: UndefVarError: ⊇ not defined

Powiązane porównania

Wiele operatorów porównania używanych razem jest połączonych w łańcuch, tak jakby byli połączeni przez operatora && . Może to być przydatne w czytelnych i matematycznie zwięzłych łańcuchach porównawczych, takich jak

# 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

Istnieje jednak istotna różnica między a > b > c oraz a > b && b > c ; w tym ostatnim termin b jest oceniany dwukrotnie. Nie ma to większego znaczenia dla zwykłych starych symboli, ale może mieć znaczenie, jeśli same terminy mają skutki uboczne. Na przykład,

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

Przyjrzyjmy się bliżej powiązanym połączeniom i ich działaniu, widząc, jak są one analizowane i przekształcane w wyrażenia . Po pierwsze, rozważmy proste porównanie, które widzimy, jest zwykłym starym wywołaniem funkcji:

julia> dump(:(a > b))
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol >
    2: Symbol a
    3: Symbol b
  typ: Any

Teraz, jeśli połączymy łańcuch porównania, zauważymy, że parsowanie uległo zmianie:

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

Po analizie wyrażenie jest następnie obniżane do ostatecznej postaci:

julia> expand(:(a > b >= c))
:(begin 
        unless a > b goto 3
        return b >= c
        3: 
        return false
    end)

i zauważamy, że jest to to samo, co dla a > b && b >= c :

julia> expand(:(a > b && b >= c))
:(begin 
        unless a > b goto 3
        return b >= c
        3: 
        return false
    end)

Liczby porządkowe

Przyjrzymy się, jak zaimplementować niestandardowe porównania, implementując niestandardowy typ, liczby porządkowe . Aby uprościć implementację, skupimy się na niewielkim podzbiorze tych liczb: wszystkie liczby porządkowe do, ale bez ε not. Nasze wdrożenie koncentruje się na prostocie, a nie szybkości; jednak wdrożenie nie jest również powolne.

Liczby porządkowe przechowujemy według ich normalnej postaci Cantor . Ponieważ arytmetyka porządkowa nie jest przemienna, najpierw przyjmiemy wspólną konwencję przechowywania najbardziej znaczących terminów.

immutable OrdinalNumber <: Number
    βs::Vector{OrdinalNumber}
    cs::Vector{Int}
end

Ponieważ postać normalna Cantora jest unikalna, możemy testować równość poprzez rekurencyjną równość:

0,5.0

W wersji v0.5 jest bardzo miła składnia do wykonywania tego zwięźle:

import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
0,5.0

W przeciwnym razie zdefiniuj funkcję w sposób bardziej typowy:

import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs

Aby zakończyć nasze zamówienie, ponieważ ten typ ma całkowite zamówienie, powinniśmy przeciążyć funkcję isless :

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

Aby przetestować nasze zamówienie, możemy stworzyć metody tworzenia liczb porządkowych. Zero, oczywiście, uzyskuje się przez brak terminów w normalnej formie Cantora:

const ORDINAL_ZERO = OrdinalNumber([], [])
Base.zero(::Type{OrdinalNumber}) = ORDINAL_ZERO

Możemy zdefiniować expω do obliczenia ω^α i użyć go do obliczenia 1 i ω:

expω(α) = OrdinalNumber([α], [1])
const ORDINAL_ONE = expω(ORDINAL_ZERO)
Base.one(::Type{OrdinalNumber}) = ORDINAL_ONE
const ω = expω(ORDINAL_ONE)

Mamy teraz w pełni funkcjonalną funkcję porządkowania liczb porządkowych:

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])

W ostatnim przykładzie widzimy, że drukowanie liczb porządkowych mogłoby być lepsze, ale wynik jest zgodny z oczekiwaniami.

Standardowi operatorzy

Julia obsługuje bardzo duży zestaw operatorów porównania. Obejmują one

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

Nie wszystkie z nich mają definicję w standardowej bibliotece Base . Są one jednak dostępne dla innych pakietów do zdefiniowania i używania w razie potrzeby.

W codziennym użyciu większość tych operatorów porównania nie ma znaczenia. Najczęściej używane są standardowe funkcje matematyczne do zamawiania; zobacz listę Składnia.

Podobnie jak większość innych operatorów w Julii, operatory porównania są funkcjami i można je wywoływać jako funkcje. Na przykład (<)(1, 2) jest identyczne w znaczeniu 1 < 2 .

Używanie ==, === i isequal

Istnieją trzy operatory równości: == , === i isequal . (Ostatni tak naprawdę nie jest operatorem, ale jest funkcją, a wszystkie operatory są funkcjami.)

Kiedy stosować ==

== to równość wartości . Zwraca wartość true gdy dwa obiekty reprezentują w ich obecnym stanie tę samą wartość.

Na przykład jest oczywiste, że

julia> 1 == 1
true

ale ponadto

julia> 1 == 1.0
true

julia> 1 == 1.0 + 0.0im
true

julia> 1 == 1//1
true

Prawa strona każdej z powyższych równości jest innego rodzaju , ale wciąż reprezentują tę samą wartość.

W przypadku obiektów zmiennych, takich jak tablice , == porównuje ich bieżącą wartość.

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

Przez większość czasu == jest właściwym wyborem.

Kiedy stosować ===

=== jest operacją znacznie bardziej rygorystyczną niż == . Zamiast równości wartości mierzy egalność. Dwa obiekty są egal, jeśli sam program ich nie rozróżnia. Tak więc mamy

julia> 1 === 1
true

ponieważ nie ma sposobu na odróżnienie 1 od innej 1 . Ale

julia> 1 === 1.0
false

ponieważ chociaż 1 i 1.0 są tej samej wartości, są różnego rodzaju, więc program może je rozróżnić.

Ponadto,

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

co z początku może wydawać się zaskakujące! Jak program mógł rozróżnić dwa wektory A i B ? Ponieważ wektory są zmienne, może modyfikować A , a następnie zachowywać się inaczej niż B Ale bez względu na to, jak modyfikuje A , A zawsze będzie zachowywać się tak samo jak samo A Więc A jest egal dla A , ale nie jest egal dla B

Kontynuuj wzdłuż tej żyły, obserwuj

julia> C = A
3-element Array{Int64,1}:
 1
 2
 3

julia> A === C
true

Przypisując A do C , mówimy, że C dokonał aliasu A Oznacza to, że stała się tylko inną nazwą dla A Wszelkie modyfikacje dokonane w A będą obserwowane również przez C Dlatego nie ma sposobu na odróżnienie A i C , więc są one egal.

Kiedy stosować isequal

Różnica między == i isequal jest bardzo subtelna. Największa różnica polega na tym, jak obsługiwane są liczby zmiennoprzecinkowe:

julia> NaN == NaN
false

Ten prawdopodobnie zaskakujący wynik jest zdefiniowany przez standard IEEE dla typów zmiennoprzecinkowych (IEEE-754). Ale nie jest to przydatne w niektórych przypadkach, takich jak sortowanie. isequal jest przewidziany dla tych przypadków:

julia> isequal(NaN, NaN)
true

Po drugiej stronie widma == traktuje zero ujemne IEEE i zero dodatnie jako tę samą wartość (również określoną przez IEEE-754). Te wartości mają jednak różne reprezentacje w pamięci.

julia> 0.0
0.0

julia> -0.0
-0.0

julia> 0.0 == -0.0
true

Ponownie dla celów sortowania, isequal rozróżnia je.

julia> isequal(0.0, -0.0)
false


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow