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