Η Common Lisp, όπως και άλλες γλώσσες, έχει ένα σύνολο κανόνων για το πώς αποφασίζεται αν δύο αντικείμενα είναι το "ίδιο". Αυτοί οι κανόνες ορίζουν τέσσερα επίπεδα, καθένα με μια συνάρτηση που εκτελεί τον έλεγχο αυτού του επιπέδου. Τα επίπεδα είναι διατεταγμένα από το αυστηρότερο στο χαλαρότερο.
eqΤο πρώτο επίπεδο είναι η ταυτότητα αντικειμένου.
Αυτή η ισότητα ελέγχεται με τη συνάρτηση eq.
Τα δύο αντικείμενα που ελέγχονται για ισότητα πρέπει να είναι το ίδιο ακριβώς αντικείμενο:
(eq 'apples 'apples) ; => T
(eq 'apples 'oranges) ; => NIL
(eq '(a b c) '(a b c) ; => NIL (these two lists have the same contents but are not the same list)
(let ((list1 '(a b c)) (list2 list1))
(eq list1 list2)) ; => T (these two lists are the same list)
eqlΤο δεύτερο επίπεδο προσθέτει την ισότητα αριθμών και χαρακτήρων.
Αυτή η ισότητα ελέγχεται με τη συνάρτηση eql.
Ο τρόπος με τον οποίο γίνεται ο έλεγχος εξαρτάται από τους τύπους των ορισμάτων:
eq είναι και eql
eql αν έχουν τον ίδιο τύπο και την ίδια τιμήeql αν αναπαριστούν τον ίδιο χαρακτήρα.(eql 1 1) ; => T
(eql 1 1/1) ; => NIL (one number is an integer, the other a rational)
(eql #\c #\c) ; => T
(eql #\c #\C) ; => NIL (case is different)
Μπορεί να αναρωτηθείς γιατί οι αριθμοί και οι χαρακτήρες δεν συγκρίνονται ως προς την ταυτότητα αντικειμένου με eq.
Το πρότυπο της Common Lisp επιτρέπει στις υλοποιήσεις να αντιγράφουν αριθμούς και χαρακτήρες, αν το επιλέξουν.
Έτσι, το 0 και το 0 μπορεί να μην είναι eq, καθώς μπορεί να είναι διαφορετικά στιγμιότυπα του αριθμού 0.
equalΤο τρίτο επίπεδο ελέγχει τη δομική ομοιότητα.
Αυτή η ισότητα ελέγχεται με equal.
Ο τρόπος με τον οποίο γίνεται ο έλεγχος εξαρτάται από τους τύπους των ορισμάτων:
eq
eql
equal αν τα στοιχεία τους είναι equal.
Αυτό γίνεται αναδρομικά.equal αν τα στοιχεία τους είναι eql
eq
equal αν είναι λειτουργικά ισοδύναμα.
(Εδώ υπάρχει περιθώριο για συμπεριφορά που εξαρτάται από την υλοποίηση, όσον αφορά τη διάκριση πεζών-κεφαλαίων στις συμβολοσειρές που αποτελούν τα συστατικά μέρη των pathnames.)eq
(equal '(a (b c)) '(a (b c))) ; => T (conses are equal if their contents are equal)
(equal "hello" "hello") ; => T
(equal "hello" "HELLO") ; => NIL
(equal #(1 2 3) #(1 2 3)) ; => NIL (arrays are equal only if eq)
(equal #P"foo/bar.md" #P"foo/bar.md") ; => T (pathnames are equal if "functionally equivalent"
equalpΤο τέταρτο και πιο χαλαρό επίπεδο ισότητας ελέγχεται με equalp.
Ο τρόπος με τον οποίο γίνεται ο έλεγχος εξαρτάται από τους τύπους:
equalp τότε είναι equalp
equalp αν έχουν την ίδια τιμή, ακόμα κι αν δεν είναι του ίδιου τύπουequalp αν τα στοιχεία τους είναι equalp.
Αυτό γίνεται αναδρομικά.equalp αν έχουν τον ίδιο αριθμό διαστάσεων, οι διαστάσεις αυτές είναι οι ίδιες και κάθε στοιχείο είναι equalp.equalp αν έχουν την ίδια κλάση και τις ίδιες υποδοχές και καθεμία από αυτές τις υποδοχές είναι equalp μεταξύ των δύο δομών.equalp αν έχουν και οι δύο την ίδια συνάρτηση :test, έχουν τα ίδια κλειδιά (συγκρινόμενα με αυτή τη συνάρτηση :test) και αυτά τα κλειδιά έχουν τις ίδιες τιμές, όπως συγκρίνονται με equalp.(equalp 1 1.0) ; => T
(equalp #\c #\C) ; => T
(equalp "hello" "HELLO") ; => T
(equalp #(1 2 3) #(1.0 2.0 3.0)) ; => T (arrays contain elements which are `equalp`)
(equal #S(TEST :SLOT1 'a :SLOT2 'b)
#S(TEST :SLOT1 'a :SLOT2 'b)) ; => T (structures of the same class with slots that have values which are `equalp`)
Οι παραπάνω είναι οι "γενικές" συναρτήσεις ισότητας.
Λειτουργούν, όπως ορίζονται, για οποιονδήποτε τύπο.
Αυτό μπορεί να φανεί χρήσιμο όταν γράφεις γενικό κώδικα που δεν γνωρίζει τους τύπους των αντικειμένων που θα συγκρίνει μέχρι τη στιγμή της εκτέλεσης.
Ωστόσο, γενικά θεωρείται "καλύτερο στυλ" να χρησιμοποιείς συναρτήσεις ισότητας ειδικές για τον τύπο, όταν γνωρίζεις τους τύπους που συγκρίνονται.
Για παράδειγμα string= αντί για equal.
Αυτές οι συναρτήσεις θα παρουσιαστούν και θα συζητηθούν στις σχετικές έννοιες.