data.queue
- キュー ¶キュー(FIFO)機能を提供します。 極めて軽量ですがスレッドセーフでないシンプルなキューと、 スレッドセーフなmtqueueがあります。基本的なキュー操作手続きは どちらのキューに対しても使うことができます。本節で説明されている 手続きをmtqueueに適用した場合、特に断りが無い限り、操作は アトミックに行われます。
また、スレッド間同期に使えるmtqueue用の手続きも用意されています。 例えばキューが空の時に読み出しスレッドをブロックさせたり キューの長さが指定値に達した場合に書き込みスレッドをブロックさせることができ、 いわゆる「チャネル」としてキューを使うことができます。
シンプルキューのAPIはSLIBのキュー実装の上位互換になっています。
要素を並びの末尾に追加するenqueue!
と、要素を並びの先頭から取り除く
dequeue!
だけでなく、要素を並びの先頭に追加するqueue-push!
も
提供されているので、スタックとしても使えます。
要素を並びの末尾からO(1)で取り除くAPIも欲しい場合は、
deque (double-ended queue)が必要です。data.ring-buffer
モジュール
は、ベクタの上に実装された(空間的にも時間的にも)効率の良いdequeを提供しています
(data.ring-buffer
- リングバッファ参照)。また、変更不可な両端キューdata.ideque
もあります(data.ideque
- 変更不可な両端キュー参照)。
また、scheme.list-queue
はポータブルなリストベースのキューのAPIを提供しています
(scheme.list-queue
- R7RSリストキュー)。
{data.queue
}
シンプルなキューのクラスです。
<queue>
: length ¶キュー中の要素の数を返す、読み取り専用のスロットです。
{data.queue
}
mtqueueのクラスです。<queue>
を継承しています。
<mtqueue>
: max-length ¶キュー中の要素数の上限を示します。
このスロットが0の場合、キューは要素を中に持つことはできませんが、同期デバイスとして動作します。 書き込みスレッドは、その要素を受け取る読み出しスレッドが現れるまでブロックし、 読み出しスレッドは、要素を渡してくれる書き込みスレッドが現れるまでブロックします。
<mtqueue>
: closed ¶論理値のフラグ。初期値は#f
です。
これが真になると、enqueue!
などによる新たなデータの挿入ができなくなります。
このスロットは読み出し専用で、enqueue/wait!
、queue-push/wait!
、
dequeue/wait!
、queue-pop/wait!
によってアトミックにセットすることが
できます。
これは、mtqueueをチャネルとして使っていて、シャットダウンする場合に有用です。
{data.queue
}
空のシンプルなキューを作って返します。
{data.queue
}
空のmtqueueを作って返します。整数がmax-lengthに与えられた場合は、
それがmax-length
スロットの値となります。
{data.queue
}
objがキューであれば(シンプルなキューでもmtqueueでも)#t
を返します。
{data.queue
}
objがmtqueueであれば#t
を返します。
{data.queue
}
objが空のキューであれば#t
を返します。
{data.queue
}
キューの中にある要素の数を返します。
{data.queue
}
キューが保持できる要素の最大数を返します。限度がない場合は#f
が返ります。
{data.queue
}
保持できる最大容量に達するまであといくつ要素を受け入れることができるかを
示す整数を返します。例えば、既にキューがいっぱいであれば0が返ります。
キューに最大容量が設定されていなければ+inf.0
が返ります。
この手続きが0以外有限値を返した場合でも、続くenqueue!
の呼び出し時点で
キューがいっぱいになっている可能性があることに注意してください。
この手続きの呼び出しとenqueue!
の
呼び出しの間に、他のスレッドがキューに要素を入れるかもしれないからです。
有限長のmtqueueに確実に要素を挿入したい場合はenqueue/wait!
を使ってください。
{data.queue
}
mtqueueからの読み出しで待っているスレッドの数を返します。
返り値は常に非負の正確な整数です。
この手続きが値を返してからその値を使うまでの間に、別のスレッドが キューに要素を挿入したら、待っているスレッドの数は変わってしまうことに 注意してください。この手続きの返り値を安全に使うには、 キューに値を挿入する部分を別に排他制御する必要があります。
(define q (make-mtqueue)) (thread-start! (make-thread (^[] (dequeue/wait! q)))) (mtqueue-num-waiting-readers q) ⇒ 1 (enqueue! q 'a) (mtqueue-num-waiting-readers q) ⇒ 0
{data.queue
}
キューqueueのコピーを返します。
{data.queue
}
objをキューqueueの末尾に追加します。
一つ以上のobjを与えることができ、その場合はそれらが順にenqueueされます。
queueがmtqueueの場合、渡されたオブジェクト全ての追加は
アトミックに行われます。すなわち、途中に別スレッドが要素を挿入する
ことはありません。さらに、max-length
スロットが正の有限値を
持っており、このenqueue!
の実行によってキューの要素数が
max-length
を越えることになる場合は、queueは
変更されずエラーとなります。
(max-length
がゼロの場合、この手続きは常にエラーとなります。
下に説明するenqueue/wait!
を使ってください。)
queueがmtqueueで既にクローズされていた場合は、キューは変更されず エラーが投げられます。
{data.queue
}
objをキューqueueの先頭に追加します。
一つ以上のobjを与えることができ、その場合はそれらが順にpushされます。
enqueue!
と同様に、queueがmtqueueの場合は
全てのオブジェクトはアトミックに追加され、またmax-length
とclosed
の値も
チェックされます。詳しくは上のenqueue!
の説明を参照してください。
{data.queue
}
objが既にqueueの中に含まれている場合にはqueueを
変更しないことを以外には、enqueue!
およびqueue-push!
と同じ
動作をします。objが含まれているかどうかの検査は
2引数の関数eq-procで行います。
queueがmtqueueの場合は
全てのオブジェクトはアトミックに追加され、またmax-length
とclosed
の値も
チェックされます。詳しくは上のenqueue!
の説明を参照してください。
{data.queue
}
キューqueueの先頭からひとつ要素を取って返します。
二つの手続きは全く同じ動作をします。queue-pop!
はqueue-push!
と
ペアで使われていることを強調したいときに使うと良いでしょう。
キューが空の場合は、fallbackが与えられていればそれが返され、 そうでなければエラーが報告されます。
キューがmtqueueでそのmax-length
がゼロの場合、
キューは常に空であるとみなされます。ゼロ長のキューを同期デバイスとして
使う場合はdequeue/wait!
を使ってください。
{data.queue
}
キューの全ての内容をリストにして返します。キューそのものは空になります。
キューが既に空の場合は空リストが返されます。
下のqueue->list
も参照してください。
{data.queue
}
キューqueueの先頭もしくは末尾の要素を返します。キューそのものは変更されません。
キューが空の場合、fallbackが与えられていればその値を返し、
そうでなければエラーを報告します。
{data.queue
}
与えられたリストlistの各要素をその順で持つようなキューを作成して返します。
デフォルトではシンプルキューが作られますが、classに<class>
の
サブクラスを渡すことで他のキュークラスのインスタンスを作ることができます。
initargsはclassのコンストラクタに渡されます。
{data.queue
}
キューqueueの内容をリストにして返します。
dequeue-all!
と異なり、キューそのものの内容は変化しません。
Gaucheではqueue->list
は新しいリストをアロケートしてキューの
内容をコピーします (dequeue-all!
はコピーをせずにキューの内部の
リストをそのまま返します)。組込みでqueue->list
を持っているScheme
実装がいくつかありますが、その中にはqueue->list
がキューの
内容をコピーすることを保証していないものがあるので、それらの処理系と
共有するコードではqueue->list
がリストをコピーすることを
あてにしない方が良いでしょう。
{data.queue
}
queue->list
と同じように、queueの内容をリストとして返しますが、
そのリストはqueueの内部構造と共有されているかもしれません。
つまり、返されたリストは続くqueueへの操作によって変更される可能性があり、
また返されたリストを変更するとqueueの一貫性は失われるでしょう。
この危険性のため、<mtqueue>
をこの手続きに渡すことは禁じられています。
もしそうしたらエラーとなります。
queueに蓄積されたデータをコピーせずに取り出したいというだけなら、
dequeue-all!
を使ってください。それは取り出すのとキューを空にする
操作をアトミックに行うので安全です。この手続きは、キューの中身を取り出すこと無く
内部にアクセスしたいという場合にだけ使って下さい。
{data.queue
}
キュー内の要素のうち述語predを満たす最初の要素を返します。
引数の順序はfind
に揃えました (他のリスト手続き参照)。
{data.queue
}
SRFI-1のany
のように、queue中の各要素に
順にpredを適用し、それが真の値 (#t
である必要はありません) に
評価されたらその値を返します。predを満たす要素が無ければ#f
が
返ります。
{data.queue
}
SRFI-1のevery
のように、queue中の各要素に
順にpredを適用し、それが#f
を返したらすぐ#f
を返します。
predに対し#f
を返す要素が無ければ、最後の要素にpredを
適用した結果が返ります。キューが空なら#f
が返ります。
{data.queue
}
キューから、述語predを満たす要素を全て取り除きます。
要素が削除された場合は#t
が、そうでなければ#f
が返されます。
引数の順序はscheme.list
のremove
に揃えました (scheme.list
- R7RSリスト参照)。
移植性に関する註:Scheme48には、述語ではなく削除するオブジェクトそのものを取る
delete-from-queue!
がありますが、引数の順序が逆(キューが先)になっています。
まぎらわしい衝突を避けるため、敢えてdelete-from-queue!
は
提供しませんでした。remove-from-queue!
を使えば、Scheme48互換の方法でも、
あるいはSRFI-1と一貫性のある方法でもdelete-from-queue!
を簡単に書けるでしょう。
{data.queue
}
これらの同期的手続きで、mtqueueを、生産者スレッドと消費者スレッドの間に立つ
「チャネル」として使うことができます。
enqueue/wait!
とqueue-push/wait!
は、
キューの要素数がmax-lengthを越える場合に、キューに空きができるまで、
dequeue/wait!
とqueue-pop/wait!
は、
キューが空の場合に、キューに要素が追加されるまで、
呼び出しスレッドをブロックします。
timeout引数で、ブロックされたスレッドのタイムアウトを指定することができます。
#f
は指定しなかった場合と同じで、キューの状態が変化するまで
無期限に待ちます。実数が渡された場合はそれが秒数と解釈され、最低限
その時間経過するまでは待ちます。渡されたのが<time>
オブジェクト
(時間参照)である場合は、そのオブジェクトが指定する絶対時刻を
経過するまで待ちます。
タイムアウトによりブロックが解かれた場合は、timeout-valで
指定した値が返されます。timeout-valの省略時値は#f
です。
enqueue/wait!
とqueue-push/wait!
は、
タイムアウトせずに操作が成功した場合は#t
を返します。
もしmtqueueが既にクローズされていたら、enqueue/wait!
and
queue-push/wait!
はキューを変更せずにエラーを投げます。
フラグのチェックとキューへの挿入はアトミックに行われるので、
チェックと挿入の間に他のスレッドがenqueueしてしまうことはありません。
dequeue/wait!
とqueue-push/wait!
はクローズ済みのmtqueueに
対しても使えます。
最後の省略可能引数close
は、もし真の値が与えられれば、キューをクローズします。
クローズはアトミックに行われるので、
enqueue/wait!
やqueue-push/wait!
を呼び出したのであれば、
objが最後にキューに追加されるデータであることが保証されます。
これはチャネルの「シャットダウン」操作と考えることができます。