學習軌道
/
Factor
Factor
/
練習
/
海濱木棧道遊戲
海濱木棧道遊戲

海濱木棧道遊戲

學習練習

簡介

機率遊戲需要隨機性的來源。Factor 的random詞彙表提供一個泛用詞random,它會配合你交給它的任何東西,另外還有用於洗牌與取樣的輔助詞,以及讓隨機性能在測試中可重現的方法。

將詞標記為inline

這個練習有些任務會定義一個詞,它接受一個 quotation(一個 [ … ] 區塊)作為引數並執行它。Factor 對此有一條鐵律:一個會call它作為引數收到的 quotation 的詞,必須宣告為inline,寫法是把inline直接放在結尾的;之後。

inline會告訴編譯器,把該詞的本體拼接進每一個呼叫位置,而不是將它獨立編譯一次。

  • 呼叫端的 quotation 會直接被縫合進來,因此無論 quotation 在堆疊上留下什麼,都會流回呼叫端,即使這個詞本身的堆疊效應並未宣告任何輸出。

這裡有一個小詞,它會推入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"

要擲出一般的骰子,可以把範圍往上挪一位:對6取random會得到 0 到 5,所以6 random 1 +會得到 1 到 6。

抽取多個值

要收集固定數量的隨機抽取結果,可以用replicate(在sequences中)重複執行一個 quotation:

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最後會call那個 quotation,所以這樣的包裝詞必須標記為inline。

說明

你在海濱木板路的遊樂場裡經營各種機率遊戲。客人擲骰子、夾娃娃機夾取獎品,牌桌則需要洗牌、發牌,全都由隨機性驅動。打烊時,你還想要一個「重播」功能,好讓有爭議的遊戲能從同一個種子重新跑一次。

1. 擲骰子

定義roll-die,讓它接受骰子的面數,並回傳1到sides(含)範圍內的點數。

6 roll-die .
! => 4   (some value from 1..6)

2. 取得獎品

定義pick-prize,讓它接受一串獎品,並隨機回傳其中一個。

{ "teddy" "goldfish" "keyring" } pick-prize .
! => "goldfish"

3. 洗牌

定義shuffle-deck,讓它接受一副牌(一個序列),並回傳牌被隨機排列後的結果。

{ 1 2 3 4 5 } shuffle-deck .
! => { 3 1 5 2 4 }

4. 發一手牌

定義deal-hand,讓它接受一副牌和一個數量n,並從牌組中回傳n張互不相同的牌。

{ 10 20 30 40 50 } 3 deal-hand .
! => { 40 10 30 }

5. 以種子重播

定義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
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Factor Exercism

準備好開始 海濱木棧道遊戲 了嗎?

註冊 Exercism,透過 47 個概念163 個練習 和真人引導來學習並精通 Factor,全部免費。