Треки
/
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 не потрібен, бо немає єдиного значення для порівняння.

Вказівки

Ми знову на кухні й удосконалюємо рецепт лазаньї з Смачнюща лазанья Лії. Завдання тут вимагають тримати в голові кілька вхідних даних одночасно, а саме тоді locals, іменовані привʼязки Factor, стають у пригоді.

1. Визначте стан приготування

Визначте cooking-status, щоб вона брала зі стека показник таймера та повертала рядок тексту (англ. string) зі станом.

  • 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 вправи та справжнє наставництво від людей, і все це безкоштовно.