Common Lisp dispose de nombreux prédicats d'égalité différents.
Cela diffère d'autres langages de programmation qui n'en ont parfois qu'un ou deux (par exemple == et ===).
Certains de ces prédicats en Common Lisp sont spécifiques à des types, tandis que d'autres sont génériques.
C'est de ces derniers qu'on parlera ici.
Il existe quatre prédicats d'égalité génériques, et ils se distinguent par la rigueur avec laquelle ils considèrent ce qui est « égal ».
Ce sont, du plus restrictif au moins restrictif : eq, eql, equal et equalp.
Voici un rapide ensemble de définitions (en laissant de côté quelques détails) :
eq : définit l'égalité comme signifiant que les deux objets sont identiques.
par exemple :(eq 'a 'a) ; => T
(eq (list 1 2) (list 1 2)) ; => NIL
eql : définit l'égalité comme signifiant deux nombres de même type et de même valeur, deux caractères identiques,
ou pour tout le reste s'ils sont eq.
par exemple :(eql 1 1) ; => T
(eql #\a #\a) ; => T
(eql 1 1.0) ; => NIL
(eql #\a #\A) ; => NIL
equal : définit l'égalité comme signifiant : deux listes sont equal si chaque élément est aussi equal ; deux strings sont equal si chaque élément est eql ; deux tableaux sont equal s'ils sont eq ; tout le reste est equal s'il est eql.
par exemple :(equal (list 1 2) (list 1 2)) ; => T
(equal #(1 2) #(1 2)) ; => NIL
(equal "foo" "foo") ; => T
equalp : définit l'égalité comme signifiant : les strings et les caractères sont comparés sans tenir compte de la casse ; les nombres sont comparés avec une certaine conversion de type ; les listes et les tableaux sont equalp si chaque élément est aussi equalp, les structures si elles sont du même type et que tous leurs emplacements sont equalp, et les tables de hachage si leurs clés et leurs valeurs sont equalp
par exemple :(equalp "foo" "FoO") ; => T
(equalp 3 3.0) ; => T
(equalp (make-a-structure :slot1 1 :slot2 2)
(make-a-structure :slot1 1 :slot2 2)) ; => T
Leslie, l'extraterrestre Lisp, programme son robot de labyrinthe (comme tout jeune extraterrestre Lisp qui se respecte) et a besoin d'un coup de main. Le labyrinthe est constitué d'une série de salles. Chaque salle propose un choix de portes. Chaque porte a besoin d'une clé. Si on utilise la mauvaise clé sur une porte, elle risque d'exploser (pas de panique pour le robot, il ne subira aucun dégât, mais effacer les traces de brûlure est assez pénible pour Leslie). Si le robot n'a pas de clé qui ouvre une quelconque porte, c'est toute la salle qui explose ! (Là encore, pas de panique : rien ne sera endommagé, mais le nettoyage sera pénible.)
Ce que tu dois faire pour aider Leslie, c'est donner au robot la bonne clé pour chaque salle. Le robot essaiera la clé sur chaque porte de la salle, dans l'ordre, et franchira la première porte qui s'ouvre.
Chaque test représente un labyrinthe et chaque assertion est une salle. Tu devras écrire des fonctions prédicats qui s'évaluent à T quand les arguments sont égaux et à NIL quand ils ne le sont pas.
Ce labyrinthe n'a qu'une seule salle. Tu dois fournir une fonction capable de dire si deux objets sont le même objet.
Par exemple :
a ; => "pizza"
b ; => "pizza"
(key-object-identity a a) ; => T
(key-object-identity a b) ; => NIL
car même si a et b semblent identiques, ce ne sont pas le même objet.
Ce labyrinthe a deux salles. La première a une porte qui peut s'ouvrir si ta clé sait dire si deux nombres sont strictement égaux. La seconde salle a besoin d'une clé qui laisse une certaine souplesse quand il s'agit de vérifier l'égalité de nombres.
Par exemple :
a ; => 13
b ; => 13.0
c ; => 13
(key-numbers a b) ; => NIL
(key-numbers a c) ; => T
(key-numbers-of-different-types a b) ; => T
car même si a et b ont la même valeur numérique, ils sont de types différents.
Ce labyrinthe a deux salles. La première a une porte qui peut s'ouvrir si ta clé sait dire si deux caractères sont strictement égaux. La seconde salle a besoin d'une clé qui permet de comparer les caractères sans tenir compte de la casse.
Par exemple :
a ; => #\X
b ; => #\x
c ; => #\X
(key-characters a b) ; => NIL
(key-characters a c) ; => T
(key-characters-case-insensitively a b) ; => T
car seuls des prédicats d'égalité très permissifs ignorent la casse quand ils comparent deux caractères.
Comme dans le labyrinthe des caractères, la première salle a besoin d'une clé qui vérifie si deux strings sont égales ; la seconde salle a besoin d'une clé qui autorise une comparaison insensible à la casse.
Par exemple :
a ; => "pizza"
b ; => "PIZZA"
c ; => "pizza"
(key-strings a b) ; => NIL
(key-strings a c) ; => T
(key-strings-case-insensitively a b) ; => T
car seuls des prédicats d'égalité très permissifs ignorent la casse quand ils comparent deux strings.
Voici un grand labyrinthe avec de nombreuses salles. Chaque salle a besoin d'une clé qui vérifie si les cons contiennent des éléments égaux. Chaque salle vérifie l'égalité de cons au contenu différent : des symboles, des nombres, des caractères, et certaines auront besoin de définitions d'égalité plus souples pour ces contenus.
Par exemple :
syms-a ; => (left . right)
syms-b ; => (up . down)
syms-c ; => (left . right)
chars-a ; => (#\x . #\y)
chars-b ; => (#\x . #\Y)
chars-c ; => (#\x . #\y)
nums-a ; => (13 . 23)
nums-b ; => (13 . 23.0)
nums-c ; => (13 . 23)
(key-conses-of-symbols syms-a syms-b) ; => NIL
(key-conses-of-symbols syms-a syms-c) ; => T
(key-conses-of-characters chars-a chars-b) ; => NIL
(key-conses-of-characters chars-a chars-c) ; => T
(key-conses-of-characters-case-insensitively
chars-a chars-b) ; => T
(key-conses-of-numbers nums-a nums-b) ; => NIL
(key-conses-of-numbers nums-a nums-c) ; => T
(key-conses-of-numbers-of-different-types
nums-a nums-b) ; => T
car même avec un prédicat d'égalité qui regarde à l'intérieur d'un cons, celui-ci peut ne pas être assez permissif pour ignorer la casse ou le type numérique lors de la comparaison.
Ce labyrinthe est plus simple, avec seulement deux salles. La première a besoin d'une clé qui vérifie si les tableaux sont les mêmes tableaux. La seconde a besoin d'une clé qui vérifie le contenu des tableaux.
Par exemple :
a ; => #[13 23]
b ; => #[13 23]
c ; => #[13 23.0]
(key-arrays a b) ; => NIL
(key-arrays a c) ; => NIL
(key-arrays-loosely a b) ; => T
(key-arrays-loosely a c) ; => T
car seuls des vérificateurs d'égalité très permissifs iront vérifier si le contenu des tableaux est égal.
Inscris-toi sur Exercism pour apprendre et maîtriser Common Lisp avec 27 concepts92 exercices, et un vrai mentorat humain, le tout gratuitement.