Ігри на удачу потребують джерела випадковості. Словник random у Factor надає одне узагальнене слово, random, яке пристосовується до того, що ми йому передаємо, а також допоміжні засоби для перемішування й вибирання та спосіб зробити випадковість відтворюваною для тестів.
inline
Деякі завдання цієї вправи визначають слово, яке приймає quotation (блок [ … ]) як аргумент і виконує його. Factor має щодо цього одне тверде правило: слово, яке застосовує call до quotation, отриманого як аргумент, потрібно оголосити як inline, що записується так: inline ставиться одразу після закривного символу ;.
inline вказує компілятору вставити тіло слова в кожне місце виклику замість того, щоб скомпілювати його окремо один раз.
Ось маленьке слово, яке кладе 10 на стек, а потім виконує переданий йому quotation:
USING: kernel math prettyprint ;
: with-ten ( quot -- ) 10 swap call ; inline
Його стековий ефект, ( quot -- ), не називає жодних вихідних даних, а проте виклик із двома quotation різних ефектів дає різні результати, і всі вони повертаються до того, хто викликав:
[ 1 + ] with-ten . ! => 11 (quotation ( n -- n ): one value back)
[ dup 2 * ] with-ten . . ! => 20
! => 10 (quotation ( n -- n n ): two values back)
random: один елементrandom (у random) узагальнений щодо свого аргументу:
random ( obj -- elt )
n, він повертає випадкове ціле число з проміжку [0, n):USING: random ;
6 random . ! 4 (a value from 0 to 5)
{ "rock" "paper" "scissors" } random . ! "paper"
Щоб кинути звичайний кубик, зсунемо діапазон на одиницю вгору: random від 6 дає від 0 до 5, тож 6 random 1 + дає від 1 до 6.
Щоб зібрати фіксовану кількість випадкових значень, повторімо quotation за допомогою replicate (у sequences):
replicate ( n quot -- seq )
USING: random sequences ;
3 [ 6 random 1 + ] replicate . ! { 4 1 6 } (three dice)
randomize: перемішування на місціrandomize (у random) повертає послідовність, елементи якої розташовані у випадковому порядку:
randomize ( seq -- randomized )
{ 1 2 3 4 5 } randomize . ! { 3 1 5 2 4 }
Він перемішує на місці й повертає ту саму послідовність, тож якщо потрібно зберегти початковий порядок, спершу застосуймо clone.
sample: кілька різних елементівsample (у random) вибирає n різних елементів із послідовності, наче роздає руку карт, де та сама карта не трапляється двічі:
sample ( seq n -- seq' )
{ 10 20 30 40 50 } 3 sample . ! { 40 10 30 }
Якщо попросити більше елементів, ніж є в послідовності, це помилка.
Типово random бере значення з глобального генератора, ініціалізованого від системного годинника, тож кожен запуск дає інший результат. Для тестів, або коли хочеться мати гру, яку можна перегравати, привʼяжімо генератор до фіксованого початкового значення.
<mersenne-twister> (у random.mersenne-twister) будує генератор із цілочисельного початкового значення, а with-random (у random) виконує quotation із цим генератором:
<mersenne-twister> ( seed -- rnd )
with-random ( rnd quot -- )
USING: random random.mersenne-twister ;
42 <mersenne-twister> [ 6 random 1 + ] with-random .
! always the same value for seed 42
Усе всередині quotation (random, randomize, sample) бере значення з ініціалізованого генератора, тож те саме початкове значення завжди відтворює той самий результат.
Слово, яке приймає quotation як аргумент і виконує його через with-random, підпадає під правило з розділу Позначення слова як inline: with-random зрештою викликає цей quotation, тож таке слово-обгортку потрібно позначити як inline.
Ми проводимо ігри на удачу в аркаді на морській набережній. Відвідувачі кидають кубики, автомат із захватом хапає приз, а картковий стіл потребує перетасованої колоди й розданих карт. Усе це працює на випадковості. Наприкінці вечора нам також потрібна можливість «повтору», щоб спірну гру можна було запустити знову з тим самим початковим значенням.
Визначте roll-die, щоб він приймав кількість сторін і повертав результат кидка в діапазоні від 1 до sides включно.
6 roll-die .
! => 4 (some value from 1..6)
Визначте pick-prize, щоб він приймав послідовність призів і повертав один із них випадково.
{ "teddy" "goldfish" "keyring" } pick-prize .
! => "goldfish"
Визначте shuffle-deck, щоб він приймав колоду (послідовність) і повертав її з картами у випадковому порядку.
{ 1 2 3 4 5 } shuffle-deck .
! => { 3 1 5 2 4 }
Визначте deal-hand, щоб він приймав колоду й кількість n і повертав n різних карт з колоди.
{ 10 20 30 40 50 } 3 deal-hand .
! => { 40 10 30 }
Визначте play-seeded, щоб він приймав початкове значення й quotation і запускав quotation із генератором, ініціалізованим цим цілим числом, тож те саме початкове значення завжди дає той самий результат. Результат quotation потрібно повернути тому, хто її викликає.
42 [ 6 roll-die ] play-seeded .
! => the same value every time for seed 42
! Same seed, same game:
99 [ 5 deal-hand ] play-seeded 99 [ 5 deal-hand ] play-seeded = .
! => t
Зареєструйтеся на Exercism, щоб вивчати й опановувати Factor, а також 47 концепцій163 вправи та справжнє наставництво від людей, і все це безкоштовно.