تحتوي لغة Common Lisp على محمولات مساواة متعددة ومختلفة.
وهذا يختلف عن لغات برمجة أخرى قد لا تملك سوى محمول واحد أو اثنين (مثل == و === على سبيل المثال).
بعض هذه المحمولات في Common Lisp خاص بأنواع معينة، وبعضها الآخر عام.
وسنتناول هنا هذه الأخيرة.
هناك أربعة محمولات مساواة عامة، وهي تختلف في مدى تقييدها لما تعتبره «متساويًا».
وهي، مرتبةً من الأكثر تقييدًا إلى الأقل تقييدًا: eq و eql و equal و equalp.
وفيما يلي مجموعة سريعة من التعريفات (مع إغفال بعض التفاصيل):
eq: يعرّف المساواة بأنها تعني أن الكائنين متطابقان.
مثال:(eq 'a 'a) ; => T
(eq (list 1 2) (list 1 2)) ; => NIL
eql: يعرّف المساواة بأنها تعني عددين لهما النوع نفسه والقيمة نفسها، أو محرفين متطابقين،
أو أي شيء آخر إن كانا eq.
مثال:(eql 1 1) ; => T
(eql #\a #\a) ; => T
(eql 1 1.0) ; => NIL
(eql #\a #\A) ; => NIL
equal: يعرّف المساواة بأنها تعني: قائمتان تكونان equal إذا كان كل عنصر فيهما equal أيضًا؛ وسلسلتان نصيتان تكونان equal إذا كان كل عنصر فيهما eql؛ ومصفوفتان تكونان equal إذا كانتا eq؛ وكل ما عدا ذلك يكون equal إذا كانا eql.
مثال:(equal (list 1 2) (list 1 2)) ; => T
(equal #(1 2) #(1 2)) ; => NIL
(equal "foo" "foo") ; => T
equalp: يعرّف المساواة بأنها تعني: تُقارَن السلاسل النصية والمحارف دون التمييز بين الحروف الكبيرة والصغيرة؛ وتُقارَن الأعداد مع شيء من تحويل الأنواع؛ والقوائم والمصفوفات تكون equalp إذا كان كل عنصر فيها equalp أيضًا، والبنى تكون equalp إذا كانت من النوع نفسه وكانت جميع خاناتها equalp، وجداول التجزئة تكون equalp إذا كانت مفاتيحها وقيمها equalp
مثال:(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
ليزلي، الفضائي من عالَم Lisp، يبرمج روبوت المتاهة الخاص به (كما اعتاد صغار فضائيي Lisp أن يفعلوا) ويحتاج إلى بعض المساعدة. تتكوّن المتاهة من سلسلة من الغرف، وفي كل غرفة مجموعة من الأبواب يمكن الاختيار بينها، ويحتاج كل باب إلى مفتاح. إذا استخدمت المفتاح الخطأ على باب فمن المحتمل أن ينفجر (لا تقلق على الروبوت، فلن يحدث أي ضرر، لكن إزالة آثار الاحتراق مزعجة جدًا لليزلي). وإذا لم يكن لدى الروبوت مفتاح يفتح أي باب فستنفجر الغرفة بأكملها! (مرة أخرى، لا تقلق، فلن يحدث ضرر، لكن تنظيفها سيكون مزعجًا).
ما عليك مساعدة ليزلي فيه هو إعطاء الروبوت المفتاح الصحيح لكل غرفة. سيستخدم الروبوت المفتاح على كل باب في الغرفة بالترتيب، ويمرّ من أول باب يُفتح.
يمثّل كل اختبار متاهة، ويمثّل كل تأكيد غرفة. ستحتاج إلى كتابة دوال تحقق يكون ناتجها T عندما تكون الوسائط متساوية و NIL عندما لا تكون كذلك.
تتكوّن هذه المتاهة من غرفة واحدة فقط. عليك تقديم دالة تستطيع تحديد ما إذا كان كائنان هما الكائن نفسه.
على سبيل المثال:
a ; => "pizza"
b ; => "pizza"
(key-object-identity a a) ; => T
(key-object-identity a b) ; => NIL
لأن a و b، رغم أنهما يبدوان متطابقين، ليسا الكائن نفسه.
تتكوّن هذه المتاهة من غرفتين. في الغرفة الأولى باب يُفتح إذا كان مفتاحك قادرًا على تحديد ما إذا كان عددان متساويين تمامًا. أما الغرفة الثانية فتحتاج إلى مفتاح يمنح بعض المرونة عند التحقق من تساوي الأعداد.
على سبيل المثال:
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
لأن a و b متساويان عدديًا لكنهما من نوعين مختلفين.
تتكوّن هذه المتاهة من غرفتين. في الغرفة الأولى باب يُفتح إذا كان مفتاحك قادرًا على تحديد ما إذا كان محرفان متساويين تمامًا. أما الغرفة الثانية فتحتاج إلى مفتاح يسمح بمقارنة المحارف دون مراعاة حالة الأحرف.
على سبيل المثال:
a ; => #\X
b ; => #\x
c ; => #\X
(key-characters a b) ; => NIL
(key-characters a c) ; => T
(key-characters-case-insensitively a b) ; => T
لأن دوال التحقق المتساهلة جدًا فقط هي التي تتجاهل حالة الأحرف عند مقارنة محرفين.
مثل متاهة المحارف، تحتاج الغرفة الأولى إلى مفتاح يتحقق مما إذا كانت سلسلتان نصيتان متساويتين؛ وتحتاج الغرفة الثانية إلى مفتاح يسمح بالمقارنة دون مراعاة حالة الأحرف.
على سبيل المثال:
a ; => "pizza"
b ; => "PIZZA"
c ; => "pizza"
(key-strings a b) ; => NIL
(key-strings a c) ; => T
(key-strings-case-insensitively a b) ; => T
لأن دوال التحقق المتساهلة جدًا فقط هي التي تتجاهل حالة الأحرف عند مقارنة سلسلتين نصيتين.
cons
هذه متاهة كبيرة فيها غرف كثيرة. تحتاج كل غرفة إلى مفتاح يتحقق مما إذا كانت أزواج cons تحتوي على الأشياء المتساوية. وستتحقق كل غرفة من تساوي أزواج cons ذات محتويات مختلفة: رموز، وأعداد، ومحارف، وسيحتاج بعضها إلى تعريفات تساوٍ أكثر مرونة لتلك المحتويات.
على سبيل المثال:
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
لأن دالة التحقق، حتى لو نظرت داخل cons، قد لا تكون متساهلة بما يكفي لتتجاهل حالة الأحرف أو النوع العددي عند المقارنة.
هذه المتاهة أبسط، ففيها غرفتان فقط. تحتاج الغرفة الأولى إلى مفتاح يتحقق مما إذا كانت المصفوفتان هما المصفوفتان نفسهما. وتحتاج الغرفة الثانية إلى مفتاح يتحقق من محتويات المصفوفات.
على سبيل المثال:
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
لأن دوال التحقق المتساهلة جدًا فقط هي التي تتحقق من تساوي محتويات المصفوفة.
سجّل في Exercism لتتعلّم وتتقن Common Lisp عبر 27 مفهومًا92 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.