بما أن اسم اللغة التي نستخدمها، Lisp، اختصار لعبارة LIST Processing، فقد يفترض المرء أن اللغة تمتلك أدوات للتعامل مع مصفوفات العناصر، وأنت محق في ذلك!
رغم أن Common Lisp يمتلك بنى بيانات أخرى غير المصفوفات، فإن المصفوفات لا تزال مستخدمة بكثافة.
المصفوفة في Common Lisp هي تسلسل من العناصر. وتُسمى أيضًا cons (لأسباب تاريخية في الغالب).
ولا يشترط أن تكون العناصر نفسها من النوع ذاته.
على سبيل المثال، يمكن أن تكون لديك مصفوفة تحتوي على 1 و two و "III".
يمكنك ببساطة كتابة مصفوفة مُقتبسة هكذا: '(1 two "III")، وسيؤدي ذلك إلى إنشاء مصفوفة وتقييمها (وهي تُقيّم إلى (1 two "III")).
هناك أيضًا دالتان رئيسيتان تُستخدمان لإنشاء المصفوفات: list و cons.
تأخذ list صفرًا أو أكثر من الوسائط وتُقيّم إلى مصفوفة مُنشأة بتلك القيم:
(list 1 'two "III") ; => (1 two "III")
تأخذ cons عنصرين وتنشئ مصفوفة يكون العنصر الأول فيها هو car (رأس المصفوفة) والعنصر الثاني هو cdr (بقية المصفوفة):
(cons 1 2) ; => (1 . 2) ;; (a list without `nil` as its `cdr` is printed in this way)
(cons 1 nil) ; => (1)
(cons 1 (cons 2 nil)) ; => (1 2)
يمكن استخدام الدالتين car و cdr للوصول إلى car و cdr في مصفوفة ما.
(الدالتان first و rest مرادفتان لـ car و cdr وتعملان بالطريقة نفسها تمامًا.)
يمكن تحديد طول المصفوفة باستخدام length.
المصفوفة الفارغة طولها صفر.
يمكن الوصول إلى أي عنصر باستخدام nth (لاحظ أن فهرسة المصفوفات تبدأ من الصفر).
ليس خطأً أن تطلب فهرسًا أكبر من طول المصفوفة.
بل تُقيّم إلى nil:
(nth 23 '(short list)) ; => nil
هناك أيضًا عدة طرق مساعدة للوصول إلى العناصر الأولى في المصفوفة: first و second و third، وهكذا حتى tenth. أليس هذا رائعًا؟
يمكن دمج مصفوفتين أو أكثر باستخدام append:
(append '(a b c) '(1 2 3)) ; => (A B C 1 2 3)
(append '(a b c) '()) ; => (A B C)
يجب أن تكون كل وسيط يُمرَّر إلى append مصفوفة.
يحتاج ليزلي، الكائن الفضائي من Lisp، إلى القيام ببعض التسوّق. ومن المهم جدًّا أن تكون لديه قائمة تسوّق: يحتاج إلى إضافة أشياء إليها وحذف أشياء منها.
لكن بالطبع لا يكفي قلم وورق عاديان مع كائن فضائي من Lisp. بل إن كلمة "List" تشكّل معظم كلمة "Lisp"! لا بدّ إذن من كتابة بعض الدوال التي تساعد على متابعة التسوّق.
هل يمكنك مساعدة ليزلي في متابعة قائمة التسوّق؟
أول شيء يحتاجه ليزلي هو إنشاء مصفوفة فارغة. ودالة تُسمّى new-list ستكون مثالية لذلك.
(new-list) ; => ()
أوه لا... لدى ليزلي بالفعل بضعة أشياء في ذهنه، لذلك يحتاج إلى دالة تأخذ ثلاثة عناصر (لحسن الحظ، ليزلي لا ينشئ إلا مصفوفة من ثلاثة عناصر. لا أكثر ولا أقل!) وتنشئ قائمة تسوّق جديدة بهذه الأشياء. اكتب دالة list-of-things تأخذ ثلاثة عناصر وتُرجع مصفوفة تحتوي عليها.
(list-of-things 'bread 'milk 'butter) ; => '(bread milk butter)
قبل الذهاب إلى المتجر، ينظر ليزلي في خزانة الطعام ليرى ما يحتاجه. ساعده بكتابة الدالة add-to-list التي تضيف عنصرًا إلى بداية المصفوفة.
(add-to-list 'butter '(bread)) ; => '(butter bread)
أثناء التسوّق، يريد ليزلي أن يعرف ما الذي يبحث عنه تاليًا. كما يحبّ أن يُلقي نظرة على المصفوفة ليرى العنصر الثاني أو الثالث أو حتى العنصر رقم 23 (رقمه المحظوظ).
first-thing إلى أول عنصر في المصفوفةsecond-thing إلى العنصر الثانيthird-thing إلى العنصر الثالثtwenty-third-thing إلى العنصر الثالث والعشرين(first-thing '(bread butter milk)) ; => 'bread
(second-thing '(bread butter milk)) ; => 'butter
(third-thing '(bread butter milk)) ; => 'milk
يريد ليزلي أن يجد العنصر الأول في المصفوفة ويحذفه من الرف. ساعده بكتابة دالة remove-first-item التي تُقيَّم إلى مصفوفة تحتوي على كل عناصر مصفوفة الإدخال ما عدا العنصر الأول.
(remove-first-item '(bread butter milk)) ; => '(butter milk)
أدرك ليزلي أنه أنشأ بالخطأ قائمتي تسوّق بدلًا من واحدة! اكتب دالة list-append تُرجع مصفوفة تحتوي على عناصر المصفوفة الأولى تليها عناصر المصفوفة الثانية.
(list-append '(bread salt) '(butter milk)) ; => '(bread salt butter milk)
بدأ ليزلي يقلق من أن رحلة التسوّق هذه ستستغرق وقتًا طويلًا. فكم عدد الأشياء الموجودة في هذه المصفوفة؟ اكتب دالة just-how-long تخبره بطول مصفوفته.
(just-how-long '(bread milk butter salt)) ; => 4
سجّل في Exercism لتتعلّم وتتقن Common Lisp عبر 27 مفهومًا92 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.