مسیرها
/
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 را تعریف کنید تا آرایه‌ای از اسم لایه‌ها بگیرد و دو مقدار برگرداند: گرم رشته‌فرنگی لازم (۵۰ گرم برای هر لایه‌ی رشته‌فرنگی) و لیتر سس لازم (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 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.