トラック
/
Factor
Factor
/
演習
/
窓口係のトリアージ
窓口係のトリアージ

窓口係のトリアージ

学習演習

はじめに

ヒープとは、常に最小の要素を把握していて、その要素をすばやく取り出せるコレクションです。優先度付きキューにうってつけの道具でもあります。優先度付きキューとは、最も長く待ったものではなく、最も重要なものが次に処理される待ち行列のことです。ヒープはheapsボキャブラリーにあります。

ヒープを作る

<min-heap>は、最小のキーが最初に出てくる空のヒープを作ります。<max-heap>はその鏡写しです。

<min-heap> ( -- min-heap )
<max-heap> ( -- max-heap )

要素を追加する

各要素は値とキーの組として格納されます。キーはFactorが順序付けに使う優先度です。heap-pushで1つ追加します。

heap-push ( value key heap -- )
USING: heaps ;

<min-heap>
"alice" 5 pick heap-push   ! value "alice", priority 5
"bob"   2 pick heap-push   ! value "bob",   priority 2

ヒープは変更可能です。heap-pushは新しいヒープを返すのではなく、その場でヒープを書き換えます。heap-pushがヒープを消費したあとに、そのヒープをスタックに戻したいときはkeepが便利です。

先頭を見る、取り出す

heap-peekは先頭の要素、つまり最小のキーを持つ値を、取り出さずに返します。heap-popはその要素を返し、さらに取り除きます。heap-empty?は何か残っているかどうかを報告するので、いつ取り出すのをやめればよいかがわかります。heap-pop-allはすべてを一度に取り除き、優先度順に並んだ{ key value }ペアの連想リストを返します。

heap-peek    ( heap -- value key )
heap-pop     ( heap -- value key )
heap-empty?  ( heap -- ? )
heap-pop-all ( heap -- alist )
USING: assocs heaps kernel prettyprint ;

<min-heap>
"alice" 5 pick heap-push
"bob"   2 pick heap-push
dup heap-empty? .        ! => f      (two items waiting)
dup heap-peek drop .     ! => "bob"  (smallest key, still in the heap)
dup heap-pop drop .      ! => "bob"  (smallest key, now removed)
heap-pop-all .           ! => { { 5 "alice" } }

heap-popとheap-empty?は、要素を1つずつ処理していくための部品です。先頭を覗くか取り出すかして、それに対して処理を行い、heap-empty?がtを返したらやめます。以下のタスクではheap-pop-allを使ってキュー全体を一度に空にしますが、ヒープが空になるまで取り出し続けても同じ順序になります。

heap-pop-allから得られるペアは連想リストです。assocsにあるvaluesを使うと、キーを捨てて値だけを取り出せます。

説明

混み合った銀行では、待っているお客さんを、来た順ではなく優先度の順に応対します。お客さんは番号札を取るときに優先度の番号を受け取り、窓口係はいつも次にいちばん小さい番号のお客さんを応対します(1がいちばん急ぎです)。最小ヒープは、順番待ちの列にうってつけです。

1. キューを開く

new-queueを定義します。何も受け取らず、新しい空の優先度付きキューを返します。

new-queue
! => an empty priority queue

2. お客さんを追加する

join-queueを定義します。これはお客さんの名前、優先度の番号、キューを受け取ります。お客さんをキューに追加して、そのキューを返すので、お客さんを次々に追加できます。

new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue
! => the queue, now holding alice (5) and bob (2)

3. 次は誰?

next-nameを定義します。キューを受け取り、次に応対すべきお客さん(いちばん小さい優先度の番号を持つお客さん)の名前を、そのお客さんを取り除かずに返します。

new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue next-name .
! => "bob"

4. 全員を応対する

serve-allを定義します。キューを受け取り、待っているすべてのお客さんの名前を、応対される順に返します。

new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue
"carol" 9 rot join-queue serve-all .
! => { "bob" "alice" "carol" }
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Factor Exercism

窓口係のトリアージを始める準備はできましたか?

Exercismに登録すれば、47個のコンセプト163個の演習、そして本物の人間によるメンタリングとともに、Factorを学んでマスターできます。すべて無料です。