Треки
/
Factor
Factor
/
Вправи
/
Садове зібрання
Садове зібрання

Садове зібрання

Навчальна вправа

Вступ

Іноді частину стану потрібно спільно використовувати кільком словам, не протягуючи його щоразу через їхні стекові ефекти. Динамічні змінні у Factor, оголошені за допомогою SYMBOL:, дають кожному слову доступ до значення, збереженого в просторі імен.

SYMBOL: і SYMBOLS:

SYMBOL: name оголошує одну змінну. SYMBOLS: a b c ; оголошує одразу кілька:

USING: namespaces ;

SYMBOLS: counter total ;

Встановлення та отримання

set-global і get-global (у namespaces) працюють із глобальним простором імен:

set-global    ( value variable -- )
get-global    ( variable -- value )
change-global ( variable quot: ( old -- new ) -- )
0 counter set-global
counter get-global .         ! => 0
counter [ 10 + ] change-global
counter get-global .         ! => 10

Для стану на рівні модуля потрібні саме варіанти з -global.

Привʼязки в межах області видимості за допомогою with-variable

Динамічна змінна підтримує також другий варіант, з областю видимості. set / get / change (без суфікса -global) читають і записують поточну динамічну область видимості, а with-variable (у namespaces) створює нову область видимості на час виконання quotation:

with-variable ( value variable quot -- )
set           ( value variable -- )
get           ( variable -- value )
change        ( variable quot: ( old -- new ) -- )

Змінна привʼязана до value, поки виконується quot; щойно він завершується, привʼязка зникає. Це дає змогу зовнішньому коду «впорснути» значення, яке внутрішній код може прочитати, не протягуючи його через кожну сигнатуру стека.

SYMBOL: greeting

"Hello" greeting [
    greeting get .                ! => "Hello"
    "Howdy" greeting set
    greeting get .                ! => "Howdy"
    greeting [ "!" append ] change
    greeting get .                ! => "Howdy!"
] with-variable

greeting get-global .    ! => f   (the scoped binding is gone)

У цій вправі ми вживаємо глобальний варіант, але вміння розпізнати паралельне сімейство set/get/change стане в пригоді, коли ми читаємо чужий код.

Змінні вектори

Оскільки динамічні змінні тримають посилання, можна зберігати в них змінну колекцію й оновлювати її на місці:

SYMBOL: history

V{ } clone history set-global
"first event" history get-global push
"second event" history get-global push
history get-global .
! => V{ "first event" "second event" }

V{ … } - літерал вектора. Перед збереженням літерала V{ } завжди викликаймо clone: літерали спільні між викликами.

find - перший збіг

find (у sequences) повертає перший елемент, для якого предикат істинний, а також його індекс:

find ( seq quot -- index elt/f )

Якщо нічого не збігається, обидва значення дорівнюють f. Відкинути індекс за допомогою nip і скористатися unless* - це стандартна комбінація «типове значення, якщо нічого не знайдено».

reject - відкидання збігів

reject (також у sequences) - протилежність до filter: він повертає елементи, які не задовольняють предикат.

Вказівки

Обʼєднання мешканців просить нас вести реєстрацію ділянок у саду. Стан зберігається у двох динамічних змінних:

  • registrations зберігає вектор кортежів plot, наразі зареєстрованих за певною особою.
  • next-id зберігає ціле число, яке треба використати для наступної реєстрації.

Кортеж plot має два поля:

поле тип
id ціле число
registered-to рядок тексту (англ. string)

1. Відкриймо сад і виведімо список реєстрацій

Визначмо open-garden, щоб ініціалізувати динамічні змінні: порожній вектор для registrations і 1 для next-id. Потім визначмо list-registrations, яка повертає поточний вектор ділянок.

open-garden
list-registrations .
! => V{ }

2. Зареєструймо ділянку

Визначмо register, щоб вона брала імʼя зі стека, створювала новий plot із наступним вільним id, додавала його до вектора registrations, збільшувала next-id на одиницю і повертала новий кортеж plot.

open-garden
"Emma Balan" register .
! => T{ plot { id 1 } { registered-to "Emma Balan" } }

list-registrations .
! => V{ T{ plot { id 1 } { registered-to "Emma Balan" } } }

Ідентифікатори ділянок мають бути унікальними й зростати навіть після звільнення: next-id ніколи не повинен використовувати те саме значення вдруге.

3. Звільнімо ділянку

Визначмо release, щоб вона брала id і вилучала відповідний запис із registrations. Звільнення невідомого id не виконує жодних дій.

open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }

4. Отримаймо зареєстровану ділянку

Визначмо get-registration, щоб вона брала id і повертала відповідний кортеж plot, або символ not-found, якщо жодна ділянка не має такого id.

open-garden
"Emma" register drop
1 get-registration .
! => T{ plot { id 1 } { registered-to "Emma" } }

7 get-registration .
! => not-found

5. Знайдімо ділянки за іменем

Визначмо find-by-name, щоб вона брала імʼя і повертала вектор усіх ділянок, наразі зареєстрованих за цією особою.

open-garden
"Emma" register drop
"Bob" register drop
"Emma" register drop
"Emma" find-by-name .
! => V{ T{ plot { id 1 } { registered-to "Emma" } }
        T{ plot { id 3 } { registered-to "Emma" } } }
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Factor Exercism

Час розпочати Садове зібрання?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Factor, а також 47 концепцій163 вправи та справжнє наставництво від людей, і все це безкоштовно.