Julia Language
Сравнения
Поиск…
Синтаксис
- x <y #, если
xстрого меньшеy - x> y #, если
xстрого большеy - x == y #, если
xравноy - x === y # альтернативно
x ≡ y, еслиxравноy - x ≤ y # альтернативно
x <= y, еслиxменьше или равноy - x ≥ y # альтернативно
x >= y, еслиxбольше или равноy - x ≠ y # альтернативно
x != y, еслиxне равноy - x ≈ y #, если
xприблизительно равноy
замечания
Будьте осторожны с переворачиванием сравнительных знаков. Julia определяет множество функций сравнения по умолчанию без определения соответствующей перевернутой версии. Например, можно запустить
julia> Set(1:3) ⊆ Set(0:5)
true
но это не сработает
julia> Set(0:5) ⊇ Set(1:3)
ERROR: UndefVarError: ⊇ not defined
Связанные сравнения
Операторы множественного сравнения, используемые вместе, связаны цепями, как если бы они были связаны с помощью оператора && . Это может быть полезно для читаемых и математически сжатых цепочек сравнения, таких как
# 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
Однако существует важное различие между a > b > c и a > b && b > c ; в последнем, термин b оценивается дважды. Это не имеет большого значения для простых старых символов, но может иметь значение, если сами термины имеют побочные эффекты. Например,
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
Давайте более подробно рассмотрим скоординированные сравнения и как они работают, видя, как они анализируются и опускаются в выражения . Во-первых, рассмотрим простое сравнение, которое мы видим только как простой старый вызов функции:
julia> dump(:(a > b))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol >
2: Symbol a
3: Symbol b
typ: Any
Теперь, если мы свяжем сравнение, мы замечаем, что синтаксический анализ изменился:
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
После разбора выражение затем опускается до его окончательной формы:
julia> expand(:(a > b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
и мы действительно отмечаем, что это то же самое, что для a > b && b >= c :
julia> expand(:(a > b && b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
Порядковые номера
Мы рассмотрим, как реализовать пользовательские сравнения путем реализации пользовательского типа, порядковых номеров . Чтобы упростить реализацию, мы сосредоточимся на небольшом подмножестве этих чисел: все порядковые числа до, но не включая ε₀. Наша реализация ориентирована на простоту, а не на скорость; однако реализация также не замедляется.
Мы храним порядковые числа по их обычной форме Кантора . Поскольку порядковая арифметика не является коммутативной, мы сначала возьмем общее соглашение о сохранении наиболее значимых терминов.
immutable OrdinalNumber <: Number
βs::Vector{OrdinalNumber}
cs::Vector{Int}
end
Так как нормальная форма Кантора единственна, мы можем проверить равенство просто через рекурсивное равенство:
В версии v0.5 есть очень хороший синтаксис для этого компактно:
import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
В противном случае определите функцию как более типичную:
import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs
Чтобы закончить наш заказ, потому что этот тип имеет полный порядок, мы должны перегрузить функцию 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
Чтобы проверить наш заказ, мы можем создать некоторые методы для создания порядковых номеров. Нуль, конечно, получается, если в нормальной кантонской форме нет терминов:
const ORDINAL_ZERO = OrdinalNumber([], [])
Base.zero(::Type{OrdinalNumber}) = ORDINAL_ZERO
Мы можем определить expω для вычисления ω^α и использовать его для вычисления 1 и ω:
expω(α) = OrdinalNumber([α], [1])
const ORDINAL_ONE = expω(ORDINAL_ZERO)
Base.one(::Type{OrdinalNumber}) = ORDINAL_ONE
const ω = expω(ORDINAL_ONE)
Теперь у нас есть полностью функциональная функция упорядочения по порядковым номерам:
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])
В последнем примере мы видим, что печать порядковых номеров может быть лучше, но результат такой, как ожидалось.
Стандартные операторы
Джулия поддерживает очень большой набор операторов сравнения. Они включают
- Все следующие последовательности юникода:
> < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣; - Все символы в пункте 1, которым предшествует точка (
.), Которая должна быть сделана поэлементно; - Операторы
<:,>:,.!, иin, которому не может предшествовать точка (.).
Не все из них имеют определение в стандартной Base библиотеке. Тем не менее, они доступны для других пакетов для определения и использования по мере необходимости.
В повседневном использовании большинство из этих операторов сравнения не имеют отношения к делу. Наиболее распространенными являются стандартные математические функции для упорядочения; см. раздел «Синтаксис» для списка.
Как и большинство других операторов в Julia, операторы сравнения являются функциями и могут быть вызваны как функции. Например, (<)(1, 2) идентично по значению 1 < 2 .
Использование ==, === и isequal
Существует три оператора равенства: == , === и isequal . (Последнее на самом деле не является оператором, но это функция, и все операторы являются функциями.)
Когда использовать ==
== - равенство значений . Он возвращает true когда два объекта представляют в их текущем состоянии одно и то же значение.
Например, очевидно, что
julia> 1 == 1
true
но, кроме того,
julia> 1 == 1.0
true
julia> 1 == 1.0 + 0.0im
true
julia> 1 == 1//1
true
Правые части каждого равенства выше имеют другой тип , но они по-прежнему представляют одно и то же значение.
Для изменяемых объектов, например массивов , == сравнивает их текущее значение.
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
В большинстве случаев == правильный выбор.
Когда использовать ===
=== намного более строгая операция, чем == . Вместо равенства ценности он измеряет эгоизм. Два объекта являются эгальными, если они не могут отличаться друг от друга самой программой. Таким образом, мы имеем
julia> 1 === 1
true
так как нет возможности рассказать 1 отдельно от другого 1 . Но
julia> 1 === 1.0
false
потому что хотя 1 и 1.0 имеют одинаковое значение, они имеют разные типы, поэтому программа может отличать их друг от друга.
Более того,
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
что может показаться на первый взгляд удивительным! Как программа могла различать два вектора A и B ? Поскольку векторы являются изменяемыми, он может изменять A , а затем он будет вести себя иначе, чем B Но независимо от того, как он изменяет A , A всегда будет вести себя так же, как сам A Таким образом, A является эгальным для A , но не имеет значения для B
Продолжая в этом ключе, наблюдайте
julia> C = A
3-element Array{Int64,1}:
1
2
3
julia> A === C
true
Назначая A на C , мы говорим, что C имеет псевдониму A То есть, это стало просто другим именем для A Любые изменения, сделанные для A будут также наблюдаться с помощью C Поэтому нет никакого способа рассказать разницу между A и C , поэтому они являются эгальными.
Когда использовать isequal
Разница между == и isequal очень тонкая. Самая большая разница в том, как обрабатываются числа с плавающей запятой:
julia> NaN == NaN
false
Этот неожиданный результат определяется стандартом IEEE для типов с плавающей точкой (IEEE-754). Но это не полезно в некоторых случаях, например, для сортировки. isequal предоставляется для таких случаев:
julia> isequal(NaN, NaN)
true
На оборотной стороне спектра == обрабатывает отрицательный ноль IEEE и положительный нуль как одно и то же значение (также как указано в IEEE-754). Однако эти значения имеют различные представления в памяти.
julia> 0.0
0.0
julia> -0.0
-0.0
julia> 0.0 == -0.0
true
Опять же, для сортировки, между ними isequal различие.
julia> isequal(0.0, -0.0)
false