108 votes

Quelle est la différence entre eq ?, eqv ?, equal ? et = dans Scheme ?

Je me demande quelle est la différence entre ces opérations dans Scheme. 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.

J'écris les différents types de commandes en Scheme, et j'obtiens les résultats suivants :

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

Pourquoi en est-il ainsi ?

190voto

Aadit M Shah Points 17951

Je vais répondre à cette question de manière progressive. Commençons par le = prédicat d'équivalence. Le site = Le prédicat est utilisé pour vérifier si deux nombres sont égaux. Si vous lui fournissez autre chose qu'un nombre, il déclenchera une erreur :

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

En eq? Le prédicat est utilisé pour vérifier si ses deux paramètres représentent le même objet en mémoire. Par exemple :

(define x '(2 3))
(define y '(2 3))
(eq? x y)         => #f
(define y x)
(eq? x y)         => #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 vers l'emplacement mémoire 0 est considérée comme la liste vide). Par conséquent, lorsque l'on compare des listes vides eq? retournera toujours #t (car ils représentent le même objet en mémoire) :

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

Maintenant, en fonction de l'implémentation eq? peut ou ne peut pas revenir #t pour les valeurs primitives telles que les nombres, les chaînes de caractères, etc. Par exemple :

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

C'est là que le eqv? Le prédicat entre en jeu. Le site eqv? est exactement le même que le eq? 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

Par conséquent, eqv? est un sur-ensemble de eq? et dans la plupart des cas, vous devriez utiliser eqv? au lieu de eq? .

Enfin, nous en arrivons à la equal? prédicat. Le site equal? est exactement le même que le prédicat eqv? sauf qu'il peut également être utilisé pour vérifier si deux listes, vecteurs, etc. ont des éléments correspondants qui satisfont à l'attribut eqv? prédicat. Par exemple :

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

En général :

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

15voto

GoZoner Points 15679

Il y a deux pages entières dans la spécification RnRS liées à eq?, eqv?, equal? and = . Voici le Projet de spécification R7RS . Regardez !

Explication :

  • = compare les nombres, 2,5 et 2,5 sont numériquement égaux.
  • equal? pour les nombres se réduit à = 2,5 et 2,5 sont numériquement égaux.
  • eq? compare les "pointeurs". Le nombre 5, dans votre implémentation de Scheme, est implémenté comme un "immédiat" (probable), donc 5 et 5 sont identiques. Le nombre 2,5 peut nécessiter l'allocation d'un "enregistrement à virgule flottante" dans votre implémentation Scheme, les deux pointeurs ne sont pas identiques.

12voto

Sylwester Points 7142

eq? es #t lorsqu'il s'agit de la même adresse/du même objet. Normalement, on pourrait s'attendre à un #t pour un même symbole, un booléen ou un objet et à un #f pour des valeurs qui sont de type différent, avec des valeurs différentes ou qui n'ont pas la même structure. Les implémentations Scheme/Lisp ont pour tradition d'intégrer les types dans leurs pointeurs et d'intégrer les valeurs dans le même espace si celui-ci est suffisant. Ainsi, certains pointeurs ne sont pas vraiment des adresses mais des valeurs, comme le char R ou le Fixnum 10 . Ceux-ci seront eq? puisque l'"adresse" est un type+valeur incorporé. Certaines implémentations réutilisent également des constantes immuables. (eq ? '(1 2 3) '(1 2 3)) peut être #f lorsqu'il est interprété mais #t lorsqu'il est compilé puisqu'il peut obtenir la même adresse. (Comme la constante String pool en Java). Pour cette raison, de nombreuses expressions impliquant eq? ne sont pas spécifiés, donc le fait qu'il évalue à #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 que sa valeur est la même. même lorsque les données sont trop volumineuses pour tenir dans un pointeur. Ainsi, pour les eqv? fait le travail supplémentaire de vérifier que le type est l'un des types supportés, que les deux sont du même type et que les objets 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, string, et bytevector, il fait récursivement equal? avec les pièces. En pratique, il retournera #t si les deux objets sont identiques. . Avant R6RS, il est dangereux d'utiliser equal? sur les structures circulaires.

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

string=? c'est comme equal? mais cela ne fonctionne que pour les chaînes de caractères. Cela pourrait être plus efficace.

6voto

Justin Ethier Points 57486

equal? compare récursivement deux objets (de n'importe quel type) pour vérifier leur égalité.

  • Notez que cela peut s'avérer coûteux pour une grande structure de données, car la liste, la chaîne de caractères, le vecteur, etc. doivent être parcourus en entier.

  • Si l'objet ne contient qu'un seul élément (EG : nombre, caractère, etc), c'est la même chose que eqv? .


eqv? teste deux objets pour déterminer si les deux sont "normalement considérés comme le même objet".

  • eqv? y eq? sont des opérations très similaires, et les différences entre elles vont être quelque peu spécifiques à l'implémentation.

eq? est la même chose que eqv? mais peuvent être capables de discerner des distinctions plus fines, et peuvent être mises en œuvre plus efficacement.

  • Selon la spécification, cela peut être mis en œuvre comme une comparaison rapide et efficace de pointeurs, par opposition à une opération plus compliquée pour les pointeurs. eqv? .

= compare les nombres pour vérifier l'égalité numérique.

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

5voto

Alan Gilbert Points 190

Vous ne mentionnez pas d'implémentation de schéma, mais dans Racket, eq? ne renvoie vrai que si les arguments font référence au même objet. Votre deuxième exemple renvoie #f parce que le système crée un nouveau nombre à virgule flottante pour chaque argument ; il ne s'agit pas du même objet.

equal? y = vérifient l'équivalence des valeurs, mais = ne s'applique qu'aux nombres.

Si vous utilisez Racket, vérifiez les points suivants aquí pour plus d'informations. Sinon, consultez la documentation de l'implémentation de votre schéma.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X