المسارات
/
Factor
Factor
/
التمارين
/
نجم اللازانيا
نجم اللازانيا

نجم اللازانيا

تمرين تعلّمي

مقدمة

التلاعب بالمكدس أمر مقبول مع الكلمات القصيرة. وعندما تكون لكلمة عدة مدخلات وتحتاج إلى الإشارة إليها بالاسم، فإن Factor يقدّم المتغيرات المحلية: ارتباطات مسمّاة تُقدَّم بـ :: (لتعريفات الكلمات) أو [| | ] (لامبدا المضمّنة).

:: ( inputs -- outputs )

استبدل : بـ ::، فتصبح أسماء المدخلات متغيرات محلية في الجسم:

USING: locals ;

:: hypotenuse ( a b -- c )
    a a *
    b b *
    + sqrt ;

قارن ذلك بنسخة تبديل المكدس:

! DOCTEST: SKIP   (shown for comparison — would re-define hypotenuse)
: hypotenuse ( a b -- c )
    [ sq ] bi@ + sqrt ;

كلاهما صالح. تتألق المتغيرات المحلية حين يُشار إلى المدخل أكثر من مرة، أو حين لا يتوافق ترتيب العمليات بطبيعته مع ترتيب المكدس.

ربط القيم الوسيطة باستخدام :>

داخل جسم :: (أو لامبدا مضمّنة [| ... |])، يسحب :> قيمة من مكدس البيانات ويربطها بمتغير محلي جديد:

value :> name      ! immutable binding
value :> name!     ! mutable binding (name! is its setter)

الصيغة غير القابلة للتغيير هي الشائعة. استخدمها لتسمية قيمة وسيطة بحيث يُقرأ الجسم من الأعلى إلى الأسفل:

USING: locals math ;

:: average-3 ( x y z -- avg )
    x y + z + :> sum
    sum 3 / ;

صيغة ! تعلن أن المتغير المحلي قابل للتغيير؛ ولاحقًا في الجسم، تعيد value name! (بدون نقطتين، وبدون :>) إسناد قيمته:

USING: kernel locals math math.order ;

:: clamped ( x lo hi -- y )
    x :> v!
    v lo < [ lo v! ] when
    v hi > [ hi v! ] when
    v ;

تتألق المتغيرات المحلية القابلة للتغيير داخل كلمات التكرار التي ستلتقي بها في تمارين لاحقة (مثل while).

[let: نطاق من أي مكان

لا يعمل :> إلا داخل نطاق معجمي، وهو ما ينشئه :: و[| ... |] تلقائيًا. لإدخال متغيرات محلية من كلمة : عادية أو عند المستمع، لفّ الكود بـ [let ... ]:

[let code :> name code :> name ... body ]

كل :> name يربط قيمة أعلى المكدس للتعبير السابق؛ وتكون الارتباطات مرئية لبقية الصيغة.

USING: locals math ;

[let 3 :> x 4 :> y x x * y y * + ] .   ! => 25

[let هو الجسر بين تعريفات : بأسلوب المكدس والارتباطات المسمّاة، وهو مفيد حين يستفيد جزء فقط من كلمة من المتغيرات المحلية.

تعبيرات لامبدا: [| inputs | body ]

داخل الاقتباس، يُدخل [| inputs | body ] متغيرات محلية يمكن للجسم الإشارة إليها بالاسم. وهذا مفيد بشكل خاص مع الكلمات عالية الرتبة مثل assoc-map، التي يستقبل اقتباسها key و value:

USING: assocs locals ;

H{ { "a" 1 } { "b" 2 } }
[| key value | key value neg ] assoc-map .
! => H{ { "a" -1 } { "b" -2 } }

يعمل النمط نفسه مع map-index (في [sequences][sequences])، الذي يستقبل اقتباسه العنصر و فهرسه. توضّح الأسماء أي معامل يؤدي أي دور:

USING: locals sequences ;

! Divide each element by its 1-based position:
{ 10 80 270 } [| elt i | elt i 1 + / ] map-index .
! => { 10 40 90 }

يتحدد أثر المكدس للامبدا بقائمة المدخلات وما ينتجه جسمها.

متى تستخدم المتغيرات المحلية

استخدم المتغيرات المحلية عندما:

  • يُستخدم المدخل نفسه في أكثر من موضع.
  • لا يتوافق الترتيب الطبيعي للعمليات مع ترتيب المكدس، وتكون أدوات dup/swap/rot لإصلاح ذلك صعبة القراءة.
  • تكون داخل لامبدا مضمّنة تمرَّر إلى كلمة عالية الرتبة تمنحك عدة قيم دفعة واحدة (كما في assoc-map).

عندما يكون الجسم تدفقًا خطيًا نظيفًا، تبقى تعريفات : العادية أقصر وقابلة للقراءة بالقدر ذاته على الأقل.

assoc-map: مدخل واحد في كل مرة

يطبّق assoc-map (في assocs) اقتباسًا على كل مدخل من مدخلات مجموعة ترابطية:

assoc-map ( assoc quot: ( key value -- newkey newvalue ) -- newassoc )

لامبدا المتغيرات المحلية هي أنظف طريقة لكتابة الاقتباس.

cond مرة أخرى

عندما تحتاج إلى التفرّع بناءً على f أو على قيمة عددية، يكون cond هو الشكل الصحيح تمامًا، فلا حاجة إلى case لأنه لا توجد قيمة واحدة للمقارنة بها.

التعليمات

لقد عُدتَ إلى المطبخ لتحسين وصفة اللازانيا الواردة في لازانيا ليا اللذيذة. تطلب منك المهام هنا التعامل مع عدة مدخلات في آنٍ واحد، وهنا بالضبط تُثبت المتغيرات المحلية (الارتباطات المسماة في Factor) قيمتها.

1. تحديد حالة الطهي

عرّف cooking-status بحيث تأخذ قراءة المؤقّت من المكدّس وتُرجع سلسلة نصية تمثّل الحالة.

  • 0 → "Lasagna is done."
  • f (لم يُضبط المؤقّت مطلقًا) → "You forgot to set the timer."
  • أي عدد آخر → "Not done, please wait."
12 cooking-status .   ! => "Not done, please wait."
0 cooking-status .    ! => "Lasagna is done."
f cooking-status .    ! => "You forgot to set the timer."

2. تقدير زمن التحضير

عرّف preparation-time بحيث تأخذ مصفوفة بأسماء الطبقات ومتوسط عدد الدقائق لكل طبقة، وتُرجع زمن التحضير الإجمالي.

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } 3 preparation-time .
! => 18

3. حساب ما يلزم من المعكرونة والصلصة

عرّف quantities بحيث تأخذ مصفوفة بأسماء الطبقات وتُرجع قيمتين: عدد غرامات المعكرونة اللازمة (50 غرامًا لكل طبقة معكرونة) وعدد لترات الصلصة اللازمة (1/5 لتر لكل طبقة صلصة).

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } quantities .s
! => 100
! => 2/5

4. إضافة المكوّن السرّي

يرسل لك صديقك قائمة مكوّنات، والعنصر الأخير فيها هو سرّه. عرّف add-secret-ingredient بحيث تأخذ قائمته وقائمتك وتُرجع قائمتك بعد إلحاق السرّ بها.

{ "noodles" "sauce" "mozzarella" "kampot pepper" }
{ "noodles" "meat" "sauce" "mozzarella" }
add-secret-ingredient .
! => { "noodles" "meat" "sauce" "mozzarella" "kampot pepper" }

5. إعادة تحجيم الوصفة

تكفي وصفة الكتاب لحصتين. عرّف scale-recipe بحيث تأخذ وصفة (جدول تجزئة يربط اسم المكوّن بكميته) وعددًا مستهدفًا من الحصص، وتُرجع وصفة جديدة مُحجّمة بما يتناسب مع ذلك.

H{ { "noodles" 200 } { "sauce" 1/2 } { "mozzarella" 1 } { "meat" 100 } }
4 scale-recipe .
! => H{ { "noodles" 400 } { "sauce" 1 } { "mozzarella" 2 } { "meat" 200 } }
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Factor Exercism

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

سجّل في Exercism لتتعلّم وتتقن Factor عبر 47 مفهومًا163 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.