المسارات
/
Common Lisp
Common Lisp
/
التمارين
/
بقايا لازانيا Lilly
بقايا لازانيا Lilly

بقايا لازانيا Lilly

تمرين تعلّمي

مقدمة

قائمة لامبدا

في Common Lisp، تُعرف قائمة وسائط الدالة باسم «قائمة لامبدا». ويمكن أن تحتوي قائمة لامبدا على وسائط من أنواع مختلفة. وتُحدَّد هذه الأنواع المختلفة باستخدام «الكلمات المفتاحية لقائمة لامبدا»، وكلها تبدأ بـ &. والأنواع الأكثر استخدامًا هي أنواع الوسائط الاختيارية والمفتاحية والمتبقية. وكل معامل في قائمة لامبدا يأتي بعد كلمة مفتاحية معيّنة سيكون من ذلك النوع. ولا يمكن استخدام الكلمة المفتاحية لقائمة لامبدا إلا مرة واحدة في قائمة لامبدا.

تُستخدم قوائم لامبدا أيضًا في تراكيب أخرى سنتناولها لاحقًا، مثل تفكيك البنية ووحدات الماكرو.

المعاملات الاختيارية

في Common Lisp، يمكن أن تحتوي الدالة على بعض الوسائط الاختيارية. وتُحدَّد هذه في قائمة لامبدا بالكلمة المفتاحية &optional لقائمة لامبدا. ويُربط المعامل بالقيمة nil إذا لم يُحدَّد. وإذا وُجدت عدة معاملات اختيارية، فتُربط بالترتيب. ويمكن تحديد قيم افتراضية للمعاملات الاختيارية. وأخيرًا، يمكن تحديد رمز لكل معامل اختياري يُربط بـ صحيح أو خطأ حسب ما إذا كان المستدعي قد زوّد الدالة بذلك المعامل (ويُشار إلى هذا باسم «معامل supplied-p»).

(defun optional-parameters (&optional x (y 'default) (z nil z-supplied-p))
  (list x y (if z-supplied-p (list :z-was-supplied z)
                             (list :z-was-not-supplied z))))

(optional-parameters)          ; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(optional-parameters 5 nil 10) ; => (5 NIL (:Z-WAS-SUPPLIED 10))

المعاملات المفتاحية

في Common Lisp، يمكن أن تحتوي الدالة على معاملات مُسمّاة (يُشار إليها باسم «المعاملات المفتاحية»). وتُحدَّد هذه في قائمة لامبدا بالكلمة المفتاحية &key لقائمة لامبدا. والمعاملات المفتاحية ليست معاملات مطلوبة. ومثل المعاملات الاختيارية، يمكن منحها قيمًا افتراضية ورموزًا تُربط بحالة «التزويد أو عدمه».

عند استدعاء دالة ذات معاملات مفتاحية، يُكتب اسم المعامل في صورة كلمة مفتاحية قبل قيمة المعامل. ويمكن للمستدعي أن يحدد المعاملات المفتاحية بأي ترتيب.

(defun keyword-parameters (&key x (y 'default) (z nil z-supplied-p))
  (list x y (if z-supplied-p (list :z-was-supplied z)
                             (list :z-was-not-supplied z))))

(keyword-parameters)            ; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :y 5)       ; => (NIL 5 (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :z 10 :x 5) ; => (5 DEFAULT (:Z-WAS-SUPPLIED 10))

ينبغي توخي الحذر عند الجمع بين المعاملات الاختيارية والمفتاحية، لأن اسم الكلمة المفتاحية والوسيط قد تستهلكهما المعاملات الاختيارية:

(defun could-be-confusing (&optional x y &key z) (list x y z))
(could-be-confusing :z 'huh?) ; => (:Z HUH? NIL)

المعاملات المتبقية

في Common Lisp، يمكن أن تحتوي الدالة على معامل يحمل «بقية» الوسائط بعد معالجة المعاملات المطلوبة أو الاختيارية. ويُحدَّد هذا المعامل بالكلمة المفتاحية &rest لقائمة لامبدا. وإذا استهلكت أنواع المعاملات الأخرى جميع وسائط الدالة، فسيُربط المعامل المتبقي بقائمة فارغة. وإذا بقيت وسائط غير مستخدمة، فسيُربط المعامل المتبقي بقائمة تحتوي على تلك الوسائط.

(defun rest-of-it (req &optional opt &rest rest) (list req opt rest))
(rest-of-it 1)         ; => (1 NIL NIL)
(rest-of-it 1 2)       ; => (1 2 NIL)
(rest-of-it 1 2 3)     ; => (1 2 (3))
(rest-of-it 1 2 3 4 5) ; => (1 2 (3 4 5))

التعليمات

من المؤكد أن ليلي، الكائن الفضائي من لغة Lisp، قد أعدّت الكثير من اللازانيا بمساعدتك. وهي تريد أن تشكرك بأن تعطيك بعض البواقي. لكن لديها أولًا بعض الملاحظات حول إحدى الدوال التي ساعدتها فيها.

في هذا التمرين ستكتب المزيد من الدوال لمساعدة ليلي في طهي اللازانيا، وكذلك لمساعدتها في تقسيم البواقي معك.

لديك ست مهام: ثلاث حول تحسين دوال طهي اللازانيا الحالية، وثلاث حول تقسيم البواقي.

1. اجعل preparation-time-in-minutes أسهل في الاستخدام

تقول ليلي إن استخدام preparation-time-in-minutes كان صعبًا بعض الشيء، إذ كان عليها أن تعدّ كل الطبقات قبل استخدام الدالة. وتتساءل ليلي إن كان بإمكانك إنشاء نسخة من الدالة تأخذ الطبقات (أي عدد منها) كوسائط، ثم تحسب زمن التحضير بناءً على عدد الطبقات. وكَتذكير، يستغرق تحضير الطبقة الواحدة 19 دقيقة.

(preparation-time 'sauce 'cheese 'right-handed-macaroni 'cheese 'sauce
                  'left-handed-macaroni 'sauce 'sauce 'cheese 'cheese)
                  ;; => 190 (because there are 10 layers.)

2. اسمح بتغيير زمن الفرن المتوقع

بينما كانت وحداتها الأبوية تخبز اللازانيا دائمًا لمدة 337 دقيقة، تعترف ليلي بأن كائنات Lisp فضائية أخرى تحب خبز لازانيتها لمدة أطول أو أقصر.

لذا سيكون من الجيد ألا تعتمد remaining-minutes-in-oven على expected-minutes-in-oven، بل أن تأخذ معاملًا اختياريًا يحدد ما إذا كان الزمن يجب أن يكون أطول أو طويلًا جدًا أو أقصر أو قصيرًا جدًا. أما الأطول أو الأقصر فيعني إضافة 100 إلى الزمن الافتراضي 337 أو طرح 100 منه. أما الطويل جدًا أو القصير جدًا فيعني إضافة 200 أو طرحها. وإذا لم يُمرَّر أي معامل، أو كانت الوسيطة 'normal'، فيجب إرجاع القيمة الافتراضية 337.

(remaining-minutes-in-oven)             ;; => 337
(remaining-minutes-in-oven :normal)     ;; => 337
(remaining-minutes-in-oven :shorter)    ;; => 237
(remaining-minutes-in-oven :very-short) ;; => 137
(remaining-minutes-in-oven :longer)     ;; => 437
(remaining-minutes-in-oven :very-long)  ;; => 537

3. تتذكر ليلي أسلوب طهي مفضلًا آخر

بعد أن أنهيت هذا التغيير بقليل، تتذكر ليلي أن هناك بعض كائنات Lisp الفضائية التي تحب أزمنة طهي قصيرة جدًا. في واقع الأمر، يريدونها نيئة. لذا الآن يجب أن تُرجع الدالة 0 إذا كانت الوسيطة NIL، وكذلك نفس القيم كما في المهمة السابقة.

(remaining-minutes-in-oven)     ;; => 337
(remaining-minutes-in-oven nil) ;; => 0

4. تقسيم البواقي

الآن بعد أن ساعدت في تحسين دوال طهي اللازانيا، هناك الكثير من اللازانيا! تريد ليلي أن تكافئك على كل مساعدتك بأن تقتسم معك البواقي. ستحتاج إلى كتابة دالة تأخذ ثلاثة معاملات: إجمالي كمية البواقي، وعدد حاويات البواقي التي وجدتها ليلي في خزائنها، ونفس العدد لك. ولأن البحث عن حاويات البواقي قد يستغرق أزمنة مختلفة، ولأن الكمية الدقيقة من البواقي قد تُحدَّد أثناء بحثك أنت وليلي في الخزائن، فقد اتفقتما على أن تأخذ الدالة الوسائط بأي ترتيب، بحيث يمكنك تدوين كل رقم حين يُحدَّد. وعندما تصبح لديك الأرقام الثلاثة، يمكنك ببساطة أن تغلق القوس الهلالي في استدعاء الدالة وتُقيّمه.

يجب أن تُرجع الدالة كمية البواقي المتبقية، أي الفرق بين وزن البواقي وإجمالي عدد الحاويات التي وجدتها أنت وليلي.

(split-leftovers :weight 20 :human 10 :alien 5) ;; => 5
(split-leftovers :weight 20 :alien 10 :human 2) ;; => 8
(split-leftovers :alien 12 :weight 20 :human 4) ;; => 4

5. وضع الافتراضات

بعد بضع رحلات ذهابًا وإيابًا إلى خزائنكما، تتفقان على أنه سيكون رائعًا لو استطاعت الدالة أن تفترض ببساطة أن لديك أو لدى ليلي... لنقل 10 حاويات، إن لم يُحدَّد ذلك.

(split-leftovers :weight 20 :human 5) ;; => 5
(split-leftovers :weight 20 :alien 5) ;; => 5
(split-leftovers :weight 20)          ;; => 0

6. الكمية القياسية من البواقي

والأفضل من ذلك، يمكن للدالة أن تفترض أن هناك بواقي كافية لتتسع في الحاويات. لذا إذا لم يُقدَّم الوزن، يجب أن تُرجع الدالة :just-split-it. ومع ذلك، إذا قُدِّم الوزن وكان nil، فيجب أن تُرجع :looks-like-someone-was-hungry.

(split-leftovers :human 5 :alien 5) ;; => :JUST-SPLIT-IT
(split-leftovers :weight NIL :human 5 :alien 5) ;; => :LOOKS-LIKE-SOMEONE-WAS-HUNGRY
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Common Lisp Exercism

مستعد لبدء بقايا لازانيا Lilly؟

سجّل في Exercism لتتعلّم وتتقن Common Lisp عبر 27 مفهومًا92 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.