Julia Language
Comparaisons
Recherche…
Syntaxe
- x <y # si
x
est strictement inférieur ày
- x> y # si
x
est strictement supérieur ày
- x == y # si
x
est égal ày
- x === y # alternativement
x ≡ y
, six
est égal ày
- x ≤ y # alternativement
x <= y
, six
est inférieur ou égal ày
- x ≥ y # alternativement
x >= y
, six
est supérieur ou égal ày
- x ≠ y # alternativement
x != y
, six
n'est pas égal ày
- x ≈ y # si
x
est approximativement égal ày
Remarques
Faites attention à ne pas renverser les signes de comparaison. Julia définit de nombreuses fonctions de comparaison par défaut sans définir la version retournée correspondante. Par exemple, on peut courir
julia> Set(1:3) ⊆ Set(0:5)
true
mais ça ne marche pas
julia> Set(0:5) ⊇ Set(1:3)
ERROR: UndefVarError: ⊇ not defined
Comparaisons en chaîne
Plusieurs opérateurs de comparaison utilisés ensemble sont chaînés, comme s'ils étaient connectés via l' opérateur &&
. Cela peut être utile pour des chaînes de comparaison lisibles et mathématiquement concises, telles que
# 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
Cependant, il existe une différence importante entre a > b > c
et a > b && b > c
; dans ce dernier cas, le terme b
est évalué deux fois. Cela n'a pas beaucoup d'importance pour les vieux symboles simples, mais pourrait avoir de l'importance si les termes eux-mêmes ont des effets secondaires. Par exemple,
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
Examinons de plus près les comparaisons chaînées et leur fonctionnement en observant comment elles sont analysées et abaissées en expressions . Tout d'abord, considérons la simple comparaison, que nous pouvons voir est juste un appel de fonction simple:
julia> dump(:(a > b))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol >
2: Symbol a
3: Symbol b
typ: Any
Maintenant, si nous enchaînons la comparaison, nous remarquons que l'analyse a changé:
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
Après l'analyse, l'expression est ensuite abaissée à sa forme finale:
julia> expand(:(a > b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
et on remarque en effet que c'est la même chose que pour a > b && b >= c
:
julia> expand(:(a > b && b >= c))
:(begin
unless a > b goto 3
return b >= c
3:
return false
end)
Nombres ordinaux
Nous examinerons comment implémenter des comparaisons personnalisées en implémentant un type personnalisé, des nombres ordinaux . Pour simplifier l'implémentation, nous allons nous concentrer sur un petit sous-ensemble de ces nombres: tous les nombres ordinaux jusqu'à mais n'incluant pas ε₀. Notre implémentation est axée sur la simplicité et non sur la rapidité. Cependant, l'implémentation n'est pas lente non plus.
Nous stockons les nombres ordinaux par leur forme normale de Cantor . Parce que l'arithmétique ordinale n'est pas commutative, nous prendrons la convention commune de stocker d'abord les termes les plus significatifs.
immutable OrdinalNumber <: Number
βs::Vector{OrdinalNumber}
cs::Vector{Int}
end
Comme la forme normale de Cantor est unique, nous pouvons tester l’égalité simplement par l’égalité récursive:
Dans la version v0.5, il existe une très bonne syntaxe pour le faire de manière compacte:
import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
Sinon, définissez la fonction comme il est plus typique:
import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs
Pour finir notre commande, car ce type a une commande totale, nous devrions surcharger la fonction 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
Pour tester notre ordre, nous pouvons créer des méthodes pour faire des nombres ordinaux. Zéro, bien sûr, est obtenu en n’ayant pas de termes dans la forme normale de Cantor:
const ORDINAL_ZERO = OrdinalNumber([], [])
Base.zero(::Type{OrdinalNumber}) = ORDINAL_ZERO
On peut définir une expω
pour calculer ω^α
, et l'utiliser pour calculer 1 et ω:
expω(α) = OrdinalNumber([α], [1])
const ORDINAL_ONE = expω(ORDINAL_ZERO)
Base.one(::Type{OrdinalNumber}) = ORDINAL_ONE
const ω = expω(ORDINAL_ONE)
Nous avons maintenant une fonction de commande entièrement fonctionnelle sur les nombres ordinaux:
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])
Dans le dernier exemple, nous voyons que l’impression des nombres ordinaux pourrait être meilleure, mais le résultat est comme prévu.
Opérateurs standard
Julia prend en charge un très grand nombre d'opérateurs de comparaison. Ceux-ci inclus
- Toutes les séquences Unicode suivantes:
> < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊ ∍ ∥ ∦ ∷ ∺ ∻ ∽ ∾ ≁ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≣ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿ ⊀ ⊁ ⊃ ⊅ ⊇ ⊉ ⊋ ⊏ ⊐ ⊑ ⊒ ⊜ ⊩ ⊬ ⊮ ⊰ ⊱ ⊲ ⊳ ⊴ ⊵ ⊶ ⊷ ⋍ ⋐ ⋑ ⋕ ⋖ ⋗ ⋘ ⋙ ⋚ ⋛ ⋜ ⋝ ⋞ ⋟ ⋠ ⋡ ⋢ ⋣ ⋤ ⋥ ⋦ ⋧ ⋨ ⋩ ⋪ ⋫ ⋬ ⋭ ⋲ ⋳ ⋴ ⋵ ⋶ ⋷ ⋸ ⋹ ⋺ ⋻ ⋼ ⋽ ⋾ ⋿ ⟈ ⟉ ⟒ ⦷ ⧀ ⧁ ⧡ ⧣ ⧤ ⧥ ⩦ ⩧ ⩪ ⩫ ⩬ ⩭ ⩮ ⩯ ⩰ ⩱ ⩲ ⩳ ⩴ ⩵ ⩶ ⩷ ⩸ ⩹ ⩺ ⩻ ⩼ ⩽ ⩾ ⩿ ⪀ ⪁ ⪂ ⪃ ⪄ ⪅ ⪆ ⪇ ⪈ ⪉ ⪊ ⪋ ⪌ ⪍ ⪎ ⪏ ⪐ ⪑ ⪒ ⪓ ⪔ ⪕ ⪖ ⪗ ⪘ ⪙ ⪚ ⪛ ⪜ ⪝ ⪞ ⪟ ⪠ ⪡ ⪢ ⪣ ⪤ ⪥ ⪦ ⪧ ⪨ ⪩ ⪪ ⪫ ⪬ ⪭ ⪮ ⪯ ⪰ ⪱ ⪲ ⪳ ⪴ ⪵ ⪶ ⪷ ⪸ ⪹ ⪺ ⪻ ⪼ ⪽ ⪾ ⪿ ⫀ ⫁ ⫂ ⫃ ⫄ ⫅ ⫆ ⫇ ⫈ ⫉ ⫊ ⫋ ⫌ ⫍ ⫎ ⫏ ⫐ ⫑ ⫒ ⫓ ⫔ ⫕ ⫖ ⫗ ⫘ ⫙ ⫷ ⫸ ⫹ ⫺ ⊢ ⊣
; - Tous les symboles du point 1, précédés d’un point (
.
), Doivent être faits élément par élément; - Les opérateurs
<:
,>:
,.!
, etin
, qui ne peut pas être précédé d'un point (.
).
Tous n'ont pas de définition dans la bibliothèque de Base
standard. Cependant, ils sont disponibles pour d'autres packages à définir et à utiliser selon les besoins.
Dans un usage quotidien, la plupart de ces opérateurs de comparaison ne sont pas pertinents. Les plus courantes sont les fonctions mathématiques standard pour la commande; voir la section Syntaxe pour une liste.
Comme la plupart des opérateurs de Julia, les opérateurs de comparaison sont des fonctions et peuvent être appelés comme fonctions. Par exemple, (<)(1, 2)
est identique à 1 < 2
.
Utiliser ==, === et isequal
Il y a trois opérateurs d'égalité: ==
, ===
et isequal
. (Le dernier n'est pas vraiment un opérateur, mais c'est une fonction et tous les opérateurs sont des fonctions.)
Quand utiliser ==
==
est une valeur d' égalité. Il renvoie true
lorsque deux objets représentent, dans leur état actuel, la même valeur.
Par exemple, il est évident que
julia> 1 == 1
true
mais de plus
julia> 1 == 1.0
true
julia> 1 == 1.0 + 0.0im
true
julia> 1 == 1//1
true
Les côtés droits de chaque égalité ci-dessus sont d'un type différent, mais ils représentent toujours la même valeur.
Pour les objets mutables, comme les tableaux , ==
compare leur valeur actuelle.
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
La plupart du temps, ==
est le bon choix.
Quand utiliser ===
===
est une opération beaucoup plus stricte que ==
. Au lieu d’égalité de valeur, elle mesure l’égalité. Deux objets sont égaux s'ils ne peuvent pas être distingués les uns des autres par le programme lui-même. Ainsi nous avons
julia> 1 === 1
true
comme il n'y a aucun moyen de distinguer un 1
d'un autre 1
. Mais
julia> 1 === 1.0
false
car bien que 1
et 1.0
aient la même valeur, ils sont de types différents et le programme peut donc les distinguer.
En outre,
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
ce qui peut paraître surprenant au premier abord! Comment le programme pourrait-il distinguer les deux vecteurs A
et B
? Comme les vecteurs sont mutables, ils pourraient modifier A
, et se comporter différemment de B
Mais peu importe comment cela modifie A
, A
se comportera toujours de la même manière que A
lui-même. Donc, A
est égal à A
, mais pas égal à B
Continuant dans cette voie, observez
julia> C = A
3-element Array{Int64,1}:
1
2
3
julia> A === C
true
En assignant A
à C
, on dit que C
a un alias A
C'est-à-dire que c'est devenu juste un autre nom pour A
Toute modification apportée à A
sera observée par C
également. Par conséquent, il n'y a aucun moyen de faire la différence entre A
et C
, donc ils sont égaux.
Quand faut-il utiliser isequal
La différence entre ==
et isequal
est très subtile. La plus grande différence réside dans la gestion des nombres à virgule flottante:
julia> NaN == NaN
false
Ce résultat, probablement surprenant, est défini par la norme IEEE pour les types à virgule flottante (IEEE-754). Mais cela n'est pas utile dans certains cas, comme le tri. isequal
est fourni pour ces cas:
julia> isequal(NaN, NaN)
true
D'un autre côté du spectre, ==
traite le zéro négatif de l'IEEE et le zéro positif comme la même valeur (également spécifiée par IEEE-754). Ces valeurs ont cependant des représentations distinctes en mémoire.
julia> 0.0
0.0
julia> -0.0
-0.0
julia> 0.0 == -0.0
true
Toujours à des fins de tri, isequal
distingue.
julia> isequal(0.0, -0.0)
false