Quelle est la différence entre éq?, Eqv?, Égal? Et = dans le schéma?

Je me demande quelle est la différence entre ces opérations. J’ai vu des questions similaires dans Stack Overflow mais elles concernent Lisp et il n’y a pas de comparaison entre trois de ces opérateurs. Donc, si cela a déjà été demandé, faites-le moi savoir.

J’écris les différents types de commandes dans Scheme et j’obtiens les sorties suivantes:

(eq? 5 5) -->#t (eq? 2.5 2.5) -->#f (equal? 2.5 2.5) --> #t (= 2.5 2.5) --> #t 

Quelqu’un peut-il expliquer pourquoi c’est le cas?

Je vais répondre à cette question progressivement. Commençons par le prédicat d’équivalence = . Le prédicat = est utilisé pour vérifier si deux nombres sont égaux. Si vous lui fournissez autre chose qu’un numéro, cela provoquera une erreur:

 (= 2 3) => #f (= 2.5 2.5) => #t (= '() '()) => error 

L’ eq? Le prédicat est utilisé pour vérifier si ses deux parameters représentent le même object en mémoire. Par exemple:

 (define x '(2 3)) (define y '(2 3)) (eq? xy) => #f (define yx) (eq? xy) => #t 

Notez cependant qu’il n’y a qu’une seule liste vide '() en mémoire (en fait, la liste vide n’existe pas en mémoire, mais un pointeur sur l’emplacement mémoire 0 est considéré comme la liste vide). D’où lors de la comparaison des listes vides eq? retournera toujours #t (car ils représentent le même object en mémoire):

 (define x '()) (define y '()) (eq? xy) => #t 

Maintenant en fonction de la mise en œuvre eq? peut ou peut ne pas retourner #t pour les valeurs primitives telles que les nombres, les chaînes, etc. Par exemple:

 (eq? 2 2) => depends upon the implementation (eq? "a" "a") => depends upon the implementation 

C’est où l’ eqv? prédicat entre en image. L’ eqv? est exactement le même que l’ eq? prédicat, sauf qu’il retournera toujours #t pour les mêmes valeurs primitives. Par exemple:

 (eqv? 2 2) => #t (eqv? "a" "a") => depends upon the implementation 

D’où l’ eqv? est un sur-ensemble de eq? et pour la plupart des cas, vous devriez utiliser eqv? au lieu de eq? .

Finalement nous arrivons à l’ equal? prédicat. L’ equal? le prédicat est exactement le même que le eqv? prédicat, sauf qu’il peut également être utilisé pour tester si deux listes, vecteurs, etc. ont des éléments correspondants qui satisfont à l’ eqv? prédicat. Par exemple:

 (define x '(2 3)) (define y '(2 3)) (equal? xy) => #t (eqv? xy) => #f 

En général:

  1. Utilisez le prédicat = lorsque vous souhaitez tester si deux nombres sont équivalents.
  2. Utilisez l’ eqv? prédicat lorsque vous souhaitez tester si deux valeurs non numériques sont équivalentes.
  3. Utilisez l’ equal? prédicat lorsque vous souhaitez tester si deux listes, vecteurs, etc. sont équivalents.
  4. N’utilisez pas l’ eq? prédicat sauf si vous savez exactement ce que vous faites.

Il y a deux pages complètes dans la spécification RNRS liée à eq?, eqv?, equal? and = eq?, eqv?, equal? and = . Voici le projet de spécification R7RS . Vérifiez-le!

Explication:

  • = compare les nombres, 2.5 et 2.5 sont numériquement égaux.
  • equal? pour les nombres réduits à = , 2,5 et 2,5 sont numériquement égaux.
  • eq? compare les «pointeurs». Le nombre 5, dans votre implémentation Scheme, est implémenté comme un “immédiat” (probable), donc 5 et 5 sont identiques. Le nombre 2.5 peut nécessiter une allocation d’un «enregistrement à virgule flottante» dans votre implémentation Scheme, les deux pointeurs ne sont pas identiques.

eq? est #t quand c’est la même adresse / object. Normalement, on peut s’attendre à #t pour le même symbole, booléen et object et #f pour les valeurs de type différent, avec des valeurs différentes, ou pas la même structure. valeurs dans le même espace si c’est assez d’espace. Ainsi, certains pointeurs ne sont pas des adresses mais des valeurs, comme le caractère R ou le Fixnum 10 . Ceux-ci seront eq? puisque l’adresse est une valeur de type + incorporée. Certaines implémentations réutilisent également des constantes immuables. (eq? ‘(1 2 3)’ (1 2 3)) pourrait être #f quand interprété mais #t quand il est compilé car il pourrait avoir la même adresse. (Comme le pool de chaînes constantes en Java). À cause de cela, de nombreuses expressions impliquant eq? ne sont pas spécifiés, donc si elle est évaluée à #t ou #f dépend de l’implémentation.

eqv? sont #t pour les mêmes choses que eq? . C’est aussi #t si c’est un nombre ou un caractère et sa valeur est la même , même si les données sont trop grandes pour tenir dans un pointeur. Ainsi pour ces eqv? Le travail supplémentaire de vérification de ce type est-il l’un des types pris en charge, les deux sont du même type et ses objects cibles ont la même valeur de données.

equal? est #t pour les mêmes choses que eqv? et si c’est un type composé comme paire, vecteur, chaîne et bytevector, il est récursif equal? avec les pièces En pratique, il retournera #t si les deux objects se ressemblent . Avant R6RS, il est dangereux d’utiliser une valeur equal? sur des structures circulaires.

= est comme eqv? mais cela ne fonctionne que pour les types numériques . Cela pourrait être plus efficace.

ssortingng=? est comme equal? , mais cela ne fonctionne que pour les chaînes. Cela pourrait être plus efficace.

equal? compare récursivement deux objects (de tout type) pour l’égalité.

  • Notez que cela peut être coûteux pour une structure de données volumineuse, car la liste complète, la chaîne, le vecteur, etc. doivent être traversés.

  • Si l’object ne contient qu’un seul élément (par exemple: nombre, caractère, etc.), cela eqv? à eqv? .


eqv? teste deux objects pour déterminer si les deux sont “normalement considérés comme le même object”.

  • eqv? et eq? sont des opérations très similaires, et les différences entre elles vont être quelque peu spécifiques à la mise en œuvre.

eq? est le même que eqv? mais peut être capable de discerner des distinctions plus fines, et peut être mis en œuvre plus efficacement.

  • Selon la spécification, cela pourrait être implémenté comme une comparaison de pointeur rapide et efficace, par opposition à une opération plus compliquée pour eqv? .

= compare les nombres pour l’égalité numérique.

  • Notez que plus de deux nombres peuvent être fournis, par exemple: (= 1 1.0 1/1 2/2)

Vous ne mentionnez pas une implémentation de schéma, mais dans Racket, eq? ne retourne true que si les arguments font référence au même object. Votre deuxième exemple génère #f car le système crée un nouveau nombre à virgule flottante pour chaque argument. ils ne sont pas le même object.

equal? et = vérifient l’équivalence de valeur, mais = ne s’applique qu’aux nombres.

Si vous utilisez Racket, vérifiez ici pour plus d’informations. Sinon, consultez la documentation de l’implémentation de votre schéma.

Pensez à eq? comme égalité de pointeur. Les auteurs du rapport veulent que ce soit aussi général que possible afin de ne pas le dire carrément parce que cela dépend de la mise en œuvre et que le dire favoriserait les implémentations à base de pointeurs. Mais ils disent

Il sera généralement possible de mettre en œuvre eq? beaucoup plus efficace que eqv?, par exemple, comme simple comparaison de pointeur

Voici ce que je veux dire. (eqv? 2 2) est garanti pour renvoyer #t mais (eq? 2 2) n’est pas spécifié. Imaginez maintenant une implémentation basée sur un pointeur. Dans ce eq? est juste la comparaison de pointeur. Puisque (eq? 2 2) n’est pas spécifié, cela signifie que cette implémentation est libre de créer une nouvelle représentation d’object mémoire pour chaque nouveau numéro lu dans le code source. eqv? doit effectivement inspecter ses arguments.

OTOH (eq 'a 'a) est #t . Cela signifie qu’une telle implémentation doit reconnaître les symboles avec des noms en double et utiliser le même object de représentation en mémoire pour tous.

Supposons qu’une implémentation ne soit pas basée sur un pointeur. Tant qu’il adhère au rapport, peu importe. Les auteurs ne veulent tout simplement pas être perçus comme dictant les spécificités des implémentations aux développeurs, ils choisissent donc soigneusement leur formulation.

C’est mon hypothèse de toute façon.

Donc, très grossièrement, eq? est l’égalité du pointeur, eqv? est-ce que les valeurs (atomiques) sont conscientes, equal? est également conscient de la structure (vérifie récursivement ses arguments, de sorte que finalement (equal? '(a) '(a)) doit être #t ), = est pour les nombres, ssortingng=? est pour les chaînes, et les détails sont dans le rapport.