Opérateur relationnel - Relational operator

En informatique , un opérateur relationnel est une construction ou un opérateur de langage de programmation qui teste ou définit une sorte de relation entre deux entités . Il s'agit notamment de l' égalité numérique ( p . ex. , 5 = 5 ) et des inégalités ( p . ex. , 4 3 ).

Dans les langages de programmation qui incluent un type de données booléen distinct dans leur système de types , comme Pascal , Ada ou Java , ces opérateurs sont généralement évalués à vrai ou faux, selon que la relation conditionnelle entre les deux opérandes est vérifiée ou non. Dans des langages tels que C , les opérateurs relationnels renvoient les entiers 0 ou 1, où 0 signifie faux et toute valeur non nulle signifie vrai.

Une expression créée à l'aide d'un opérateur relationnel forme ce que l'on appelle une expression relationnelle ou une condition . Les opérateurs relationnels peuvent être considérés comme des cas particuliers de prédicats logiques .

Égalité

Usage

L'égalité est utilisée dans de nombreuses constructions de langage de programmation et types de données. Il est utilisé pour tester si un élément existe déjà dans un ensemble , ou pour accéder à une valeur via une clé. Il est utilisé dans les instructions de commutation pour répartir le flux de contrôle vers la branche appropriée et pendant le processus d'unification dans la programmation logique.

Une signification possible de l'égalité est que « si a est égal à b , alors a ou b peut être utilisé de manière interchangeable dans n'importe quel contexte sans remarquer de différence ». Mais cette affirmation ne tient pas nécessairement, en particulier lorsqu'on prend en compte la mutabilité avec l'égalité du contenu.

Égalité de lieu vs égalité de contenu

Parfois, en particulier dans la programmation orientée objet , la comparaison soulève des questions de types de données et d' héritage , d' égalité et d' identité . Il faut souvent distinguer :

  • deux objets différents du même type, par exemple, deux mains
  • deux objets étant égaux mais distincts, par exemple, deux billets de 10 $
  • deux objets étant égaux mais ayant une représentation différente, par exemple, un billet de 1 $ et une pièce de 1 $
  • deux références différentes au même objet, par exemple, deux surnoms pour la même personne

Dans de nombreux langages de programmation modernes, les objets et les structures de données sont accessibles via des références . Dans ces langages, il devient nécessaire de tester deux types d'égalité différents :

  • Egalité de localisation (identité) : si deux références (A et B) référencent le même objet. Les interactions avec l'objet via A sont indiscernables des mêmes interactions via B, et en particulier les changements apportés à l'objet via A sont reflétés via B.
  • Égalité de contenu : si les objets référencés par deux références (A et B) sont équivalents dans un certain sens :
  • Égalité structurelle (c'est-à-dire que leur contenu est le même). qui peut être soit superficielle (tester uniquement les sous-parties immédiates), soit profonde (tester l'égalité des sous-parties de manière récursive). Un moyen simple d'y parvenir consiste à utiliser l'égalité de représentation : vérifier que les valeurs ont la même représentation.
  • D'autres égalités sur mesure, préservant le comportement extérieur. Par exemple, 1/2 et 2/4 sont considérés comme égaux lorsqu'ils sont considérés comme un nombre rationnel. Une exigence possible serait que "A = B si et seulement si toutes les opérations sur les objets A et B auront le même résultat", en plus de la réflexivité , de la symétrie et de la transitivité .

Le premier type d'égalité implique généralement le second (sauf pour des choses comme pas un nombre ( NaN ) qui sont inégaux à eux-mêmes), mais l'inverse n'est pas nécessairement vrai. Par exemple, deux objets chaîne peuvent être des objets distincts (inégaux dans le premier sens) mais contenir la même séquence de caractères (égaux dans le second sens). Voir l' identité pour plus d'informations sur ce problème.

Les nombres réels, y compris de nombreuses fractions simples , ne peuvent pas être représentés exactement en arithmétique à virgule flottante , et il peut être nécessaire de tester l'égalité dans une tolérance donnée. Une telle tolérance, cependant, peut facilement briser les propriétés souhaitées telles que la transitivité, alors que la réflexivité se brise également : la norme à virgule flottante IEEE exige que NaN NaN soit vérifié .

D'autres éléments de programmation, tels que les fonctions calculables, peuvent soit n'avoir aucun sens d'égalité, soit avoir une égalité non calculable. Pour ces raisons, certains langages définissent une notion explicite de "comparable", sous la forme d'une classe de base, d'une interface, d'un trait ou d'un protocole, qui est utilisé soit explicitement, par déclaration dans le code source, soit implicitement, via la structure du type concerné.

Comparer des valeurs de différents types

Dans JavaScript , PHP , VBScript et quelques autres langages à typage dynamique , l'opérateur d'égalité standard est évalué à vrai si deux valeurs sont égales, même si elles ont des types différents, ce qui rend le nombre 4 comparable à la chaîne de texte "4", par exemple . Un opérateur d'égalité typé est également souvent disponible, dans ces langages, ne renvoyant vrai que pour les valeurs de types identiques ou équivalents (en PHP, 4 === "4"est faux bien qu'il 4 == "4"soit vrai). Pour les langues où le nombre 0 peut être interprété comme false , cet opérateur peut simplifier des choses telles que la vérification de zéro (comme ce x == 0serait vrai pour x étant 0 ou "0" en utilisant l'opérateur d'égalité agnostique de type).

Commande

Une comparaison supérieure et inférieure à des données non numériques est effectuée selon une convention de tri (telle que, pour les chaînes de texte, l' ordre lexicographique ) qui peut être intégrée au langage de programmation et/ou configurable par un programmeur.

Lorsqu'on souhaite associer une valeur numérique au résultat d'une comparaison entre deux éléments de données, disons a et b , la convention habituelle est d'attribuer -1 si a < b, 0 si a = b et 1 si a > b. Par exemple, la fonction C strcmpeffectue une comparaison à trois et renvoie -1, 0 ou 1 selon cette convention, et qsort s'attend à ce que la fonction de comparaison renvoie des valeurs selon cette convention. Dans les algorithmes de tri , l'efficacité du code de comparaison est critique car c'est l'un des principaux facteurs contribuant aux performances de tri.

La comparaison des types de données définis par le programmeur ( types de données pour lesquels le langage de programmation n'a aucune compréhension intégrée) peut être effectuée par des fonctions écrites sur mesure ou par des bibliothèques (telles que strcmpmentionnées ci-dessus), ou, dans certains langages, en surchargeant une comparaison opérateur - c'est-à-dire attribuer une signification définie par le programmeur qui dépend des types de données comparés. Une autre alternative consiste à utiliser une convention telle que la comparaison par membre.

Équivalence logique

Bien qu'ils ne soient peut-être pas évidents au début, comme les opérateurs logiques booléens XOR, AND, OR et NOT, les opérateurs relationnels peuvent être conçus pour avoir une équivalence logique , de sorte qu'ils peuvent tous être définis les uns par rapport aux autres. Les quatre instructions conditionnelles suivantes ont toutes la même équivalence logique E (soit toutes vraies, soit toutes fausses) pour toutes les valeurs x et y données :

Cela repose sur le domaine étant bien ordonné .

Opérateurs relationnels standards

Les opérateurs relationnels numériques les plus couramment utilisés dans les langages de programmation sont indiqués ci-dessous.

Opérateurs relationnels communs
Convention égal à pas égal à plus grand que moins que supérieur
ou égal à
inférieur
ou égal à
Sur papier = > <
FORTRAN .EQ. .NE. .GT. .LT. .GE. .LE.
ALGOL 68 = > <
/= >= <=
eq ne gt lt ge le
APL = > <
BASIQUE , ML , Pascal = <> > < >= <=
OREILLONS = '= > < '< '>
Lua == ~= > < >= <=
C-comme == != > < >= <=
Erlang == /= > < >= =<
=:= =/=
Bourne comme des coquillages -eq -ne -gt -lt -ge -le
Fichier de commandes EQU NEQ GTR LSS GEQ LEQ
MATLAB == ~= > < >= <=
eq(x,y) ne(x,y) gt(x,y) lt(x,y) ge(x,y) le(x,y)
Fortran 90 , Haskell == /= > < >= <=
Mathématique == != > < >= <=
Equal[x,y] Unequal[x,y] Greater[x,y] Less[x,y] GreaterEqual[x,y] LessEqual[x,y]

D'autres conventions sont moins courantes : Common Lisp et Macsyma / Maxima utilisent des opérateurs de type Basic sauf pour l'inégalité, qui est /=en Common Lisp et #en Macsyma/Maxima. Lisps plus anciens utilisés equal, greaterp, et lessp; et les a niés en utilisant notpour les opérateurs restants.

Syntaxe

Les opérateurs relationnels sont également utilisés dans la littérature technique à la place des mots. Les opérateurs relationnels sont généralement écrits en notation infixe , s'ils sont pris en charge par le langage de programmation, ce qui signifie qu'ils apparaissent entre leurs opérandes (les deux expressions étant liées). Par exemple, une expression en Python imprimera le message si le x est inférieur à y :

if x < y:
    print("x is less than y in this example")

D'autres langages de programmation, tels que Lisp , utilisent la notation préfixe , comme suit :

(>= X Y)

Chaînage des opérateurs

En mathématiques, il est courant d'enchaîner les opérateurs relationnels, comme dans 3 < x < y < 20 (ce qui signifie 3 < x et x < y et y < 20). La syntaxe est claire puisque ces opérateurs relationnels en mathématiques sont transitifs.

Cependant, de nombreux langages de programmation récents verraient une expression comme 3 < x < y comme composée de deux opérateurs associatifs gauche (ou droit), l'interprétant comme quelque chose comme (3 < x) < y. Si nous disons que x=4, nous obtenons alors (3 < 4) < y, et l'évaluation donnera true < yce qui n'a généralement pas de sens. Cependant, il compile en C/C++ et dans d'autres langages, donnant un résultat surprenant (comme vrai serait représenté par le numéro 1 ici).

Il est possible de donner à l'expression x < y < zsa signification mathématique familière, et certains langages de programmation tels que Python et Raku le font. D'autres, tels que C# et Java, ne le font pas, en partie parce que cela différerait de la façon dont la plupart des autres opérateurs d'infixe fonctionnent dans les langages de type C. Le langage de programmation D ne le fait pas car il maintient une certaine compatibilité avec C, et "Autoriser les expressions C mais avec une sémantique subtilement différente (bien que sans doute dans la bonne direction) ajouterait plus de confusion que de commodité".

Certains langages, comme Common Lisp , utilisent plusieurs prédicats d'arguments pour cela. En Lisp (<= 1 x 10)est vrai lorsque x est compris entre 1 et 10.

Confusion avec les opérateurs d'affectation

Le premier FORTRAN (1956-57) était limité par des jeux de caractères très restreints où =était le seul opérateur relationnel disponible. Il n'y avait pas de <ou >(et certainement pas de ou ). Cela a obligé les concepteurs à définir des symboles tels que .GT., .LT., .GE., .EQ.etc. et a par la suite rendu tentant d'utiliser le =caractère restant pour la copie, malgré l'incohérence évidente avec l'usage mathématique ( X=X+1devrait être impossible).

International Algebraic Language (IAL, ALGOL 58 ) et ALGOL (1958 et 1960) ont ainsi introduit :=pour l'affectation, laissant la norme =disponible pour l'égalité, une convention suivie par CPL , ALGOL W , ALGOL 68 , Basic Combined Programming Language ( BCPL ), Simula , SET Language ( SETL ), Pascal , Smalltalk , Modula-2 , Ada , Standard ML , OCaml , Eiffel , Object Pascal ( Delphi ), Oberon , Dylan , VHSIC Hardware Description Language ( VHDL ) et plusieurs autres langages.

B et C

Ce standard de facto uniforme parmi la plupart des langages de programmation a finalement été modifié, indirectement, par un langage compilé minimaliste nommé B . Sa seule application prévue était comme véhicule pour un premier portage d' Unix (alors très primitif) , mais il a également évolué vers le langage C très influent .

B a commencé comme une variante syntaxiquement modifiée du langage de programmation système BCPL , une version simplifiée (et sans type) de CPL . Dans ce qui a été décrit comme un processus de "strip-down", les opérateurs andet orde BCPL ont été remplacés par &et |(qui deviendra plus tard &&et ||, respectivement.). Dans le même processus, le style ALGOL :=de BCPL a été remplacé par =en B. La raison de tout cela étant inconnue. Comme les mises à jour des variables n'avaient pas de syntaxe spéciale en B (comme letou similaire) et étaient autorisées dans les expressions, cette signification non standard du signe égal signifiait que la sémantique traditionnelle du signe égal devait désormais être associée à un autre symbole. Ken Thompson a utilisé la ==combinaison ad hoc pour cela.

Comme un petit système de types a été introduit plus tard, B est alors devenu C. La popularité de ce langage, ainsi que son association avec Unix, ont conduit Java, C# et de nombreux autres langages à emboîter le pas, syntaxiquement, malgré ce conflit inutile avec le sens mathématique de le signe égal.

Langues

Les affectations en C ont une valeur et puisque toute valeur scalaire non nulle est interprétée comme vraie dans les expressions conditionnelles , le code if (x = y)est légal, mais a une signification très différente de if (x == y). L'ancien fragment de code signifie "attribuez y à x et si la nouvelle valeur de x n'est pas zéro, exécutez l'instruction suivante". Ce dernier fragment signifie " si et seulement si x est égal à y , exécutez l'instruction suivante ".

  int x = 1;
  int y = 2;
  if (x = y) {
      /* This code will always execute if y is anything but 0*/
      printf("x is %d and y is %d\n", x, y);
  }

Bien que Java et C# aient les mêmes opérateurs que C, cette erreur provoque généralement une erreur de compilation dans ces langages, car la condition if doit être de type boolean, et il n'y a aucun moyen implicite de convertir d'autres types ( par exemple , des nombres) en booleans. Donc, à moins que la variable affectée à ait un type boolean(ou un type d'enveloppe Boolean), il y aura une erreur de compilation.

Dans les langages de type ALGOL tels que Pascal, Delphi et Ada (dans le sens où ils autorisent les définitions de fonctions imbriquées ), et dans Python , et de nombreux langages fonctionnels, entre autres, les opérateurs d'affectation ne peuvent pas apparaître dans une expression (y compris les ifclauses), ainsi excluant cette catégorie d'erreur. Certains compilateurs, tels que GNU Compiler Collection (GCC), fournissent un avertissement lors de la compilation de code contenant un opérateur d'affectation dans une instruction if, bien qu'il existe des utilisations légitimes d'une affectation dans une condition if. Dans de tels cas, l'affectation doit être encapsulée explicitement dans une paire de parenthèses supplémentaire, pour éviter l'avertissement.

De même, certains langages, tels que BASIC, n'utilisent que le =symbole pour l'affectation et l' égalité, car ils sont syntaxiquement séparés (comme avec Pascal, Ada, Python, etc., les opérateurs d'affectation ne peuvent pas apparaître dans les expressions).

Certains programmeurs prennent l'habitude d'écrire des comparaisons par rapport à une constante dans l'ordre inverse de l'ordre habituel :

  if (2 == a) {   /* Mistaken use of = versus == would be a compile-time error */
  }

Si =est utilisé accidentellement, le code résultant est invalide car 2 n'est pas une variable. Le compilateur générera un message d'erreur auquel l'opérateur approprié pourra être substitué. Ce style de codage est appelé comparaison de gauche ou conditions Yoda .

Ce tableau répertorie les différents mécanismes pour tester ces deux types d'égalité dans différentes langues :

Langue Égalité physique Égalité structurelle Remarques
ALGOL 68 a :=: b ou alors a is b a = b quand aet bsont des pointeurs
C , C++ a == b *a == *b quand aet bsont des pointeurs
C# object.ReferenceEquals(a, b) a.Equals(b) L' ==opérateur par défaut est ReferenceEquals, mais peut être surchargé pour s'exécuter à la Equalsplace.
Lisp commun (eq a b) (equal a b)
Erlang a =:= b a == b quand a et b sont des nombres
Va a == b reflect.DeepEqual(*a, *b) quand a et b sont des pointeurs
Java a == b a.equals(b)
JavaScript a === b a == b lorsque a et b sont deux objets chaîne contenant des caractères équivalents, l'opérateur === retournera toujours vrai.
OCaml , Smalltalk a == b a = b
Pascal a^ = b^ a = b
Perl $a == $b $$a == $$b quand $aet $bsont des références à des scalaires
PHP $a === $b $a == $b quand $aet $bsont des objets
Python a is b a == b
Rubis a.equal?(b) a == b
Schème (eq? a b) (equal? a b)
Rapide a === b a == b quand a et b ont le type de classe
Visual Basic .NET a Is b ou alors object.ReferenceEquals(a, b) a = b ou alors a.Equals(b) Identique à C#
Objective-C ( Cacao , GNUstep ) a == b [a isEqual:b] quand aet bsont des pointeurs vers des objets qui sont des instances deNSObject

Ruby a === bveut dire "b est membre de l'ensemble a", bien que les détails de ce que cela signifie d'être membre varient considérablement en fonction des types de données impliqués. ===est ici appelé opérateur « égalité de cas » ou « subsomption de cas ».

Voir également

Notes et références