gauche.generator
- ジェネレータ ¶ジェネレータは、値の列の生成器として動作する、引数を取らない手続きです。
呼ばれる度に列の次の値を返します。列の終端に達した場合はEOFが返されます。
例えば、read-char
は、現在の入力ポートからの入力を一文字づつ返す
ジェネレータと考えることができます。
値の列の生成源を手続きによってジェネレータとして抽象化するのは広く使われるテクニックなので、 このようなジェネレータに共通して使えるユーティリティがあると便利です。 このモジュールはそのために作られました。
SRFI-121 (Generators) はこのモジュールのサブセットです。
gauche.generator
はSRFI-121より前からあったので、
いくつかの手続きについては異なる名前を使っていました。互換性のため、
それらの手続きは両方の名前を定義しています。
SRFI-158 (Generators and accumulators)はさらに追加のジェネレータ手続きを
定義していますが、それらもこのモジュールに含まれています
(ただし、アキュムレータについてはSRFI-158モジュールに分けてあります。
scheme.generator
- R7RSジェネレータ参照)。
ジェネレータは、必要になったら計算を行うようなシステムを簡単に実現でき、 効率も極めて良いですが、副作用に頼った抽象化であることには注意が必要です。 例えば、途中まで列を生成した後に、最初にもどってやり直す、といったことはできません。 より関数的にオンデマンドの計算を行うためには、ジェネレータを使って 構築された、遅延シーケンスを使うのが便利です (遅延シーケンス参照)。
ジェネレータの典型的な使い方は次の通りです。まず値の源となる ジェネレータを作ります。これはジェネレータの生成手続きを使っても良いですし (ジェネレータの生成参照)、自分で一から定義しても良いでしょう。 次に、生成される値を流れの途中で加工するジェネレータ操作手続きを 必要に応じて繋いでゆきます(ジェネレータの操作参照)。 最終的には、ジェネレータから具体的な値を取り出して消費する必要があります。 そのために便利なジェネレータ消費手続きも用意してあります ジェネレータの消費参照)。このように、ジェネレータ手続きを組み合わせた パイプライン(あるいは有向非循環グラフ)を作ることで、怠惰な値伝搬ネットワークを 実現できます。
• ジェネレータの生成: | ||
• ジェネレータの操作: | ||
• ジェネレータの消費: |
ジェネレータは特別なデータタイプではなく、普通の手続きに過ぎません。つ まり、ジェネレータはlambdaを使って作ることができます。 このモジュールは、あると便利な 良く使われるジェネレータの構築子を提供します。
自分で定義した手続きをジェネレータとして使うこともできますが、 ジェネレータを扱う手続きは、 一度EOFを返したジェネレータを繰り返して呼び出す可能性があることに注意してください。 その手続きがいったんEOFを返したら、それ以降の呼び出しに対してEOFを 返し続けるよう書かなければなりません。
ジェネレータ構築子が返すのは単なる手続きであり、そのまま印字しても
中身が何かはわかりません。この節の例では、generator->list
を使って
ジェネレータをリストに変換しています。generator->list
の説明は、
ジェネレータの消費を参照してください。
{gauche.generator
}
空ジェネレータです。呼ばれるたびにEOFオブジェクトを返します。
[SRFI-158]{gauche.generator
}
与えられた引数を繰り返し生成する、無限ジェネレータを返します。
(generator->list (circular-generator 1 2 3) 10) ⇒ (1 2 3 1 2 3 1 2 3 1)
この例では、変換後のリストの長さを10に制限しています。そうしなければ、
generator->list
は制御を返さないでしょう。
{gauche.generator
}
iota
(see リストの作成)のような、startで始まり
stepずつ増加する、count個の級数のジェネレータを作成します。
(generator->list (giota 10 3 2)) ⇒ (3 5 7 9 11 13 15 17 19 21)
startとendがともに正確数であれば、ジェネレータは 正確数を生成します。そうでなければ非正確数を生成します。
(generator->list (giota +inf.0 1/2 1/3) 6) ⇒ (1/2 5/6 7/6 3/2 11/6 13/6) (generator->list (giota +inf.0 1.0 2.0) 5) ⇒ (1.0 3.0 5.0 7.0 9.0)
{gauche.generator
}
giota
と同様、級数ジェネレータを作成します。この級数は
startに始まり、stepずつ増加してend直前まで続きます。
(generator->list (grange 3 8)) ⇒ (3 4 5 6 7)
{gauche.generator
}
コルーチンからジェネレータを作成します。
引数procは、引数yieldひとつを取る手続きです。
generate
は、呼ばれるとジェネレータGをただちに返します。
Gは呼ばれると、その中でyieldが呼ばれるまでprocを
実行します。yieldが呼ばれるとprocの実行は中断され、
yieldに渡した値がGから返ります。
いったんprocが返ると、それが列の終端となります—それ以降、 GはEOFオブジェクトを返します。procが返す値は無視されます。
次に挙げるコードは、0、1、2からなる級数を生成するジェネレータを作成し
(事実上、(giota 3)
と同じ)、g
に束縛しています。
(define g (generate (^[yield] (let loop ([i 0]) (when (< i 3) (yield i) (loop (+ i 1))))))) (generator->list g) ⇒ (0 1 2)
[SRFI-158+]{gauche.generator
}
実引数の各要素を生成するジェネレータを返します。
reverse-*
は、逆順で値を生成します。
SRFI-121はuvector->generator
以外の手続きを定義しています。
uvector->generator
は全ての種類のユニフォームベクタを取ることができます。
一方、SRFI-121も定義するbytevector->generator
はu8vector
だけを
対象とします。
(generator->list (list->generator '(1 2 3 4 5))) ⇒ (1 2 3 4 5) (generator->list (vector->generator '#(1 2 3 4 5))) ⇒ (1 2 3 4 5) (generator->list (reverse-vector->generator '#(1 2 3 4 5))) ⇒ (5 4 3 2 1) (generator->list (string->generator "abcde")) ⇒ (#\a #\b #\c #\d #\e) (generator->list (uvector->generator '#u8(1 2 3 4 5))) ⇒ (1 2 3 4 5)
いったん全ての要素を取り出してしまえば そのジェネレータは空になります;省略可能引数startとendで ジェネレータがたどる範囲を制限することができます;startで 左の境界を、endで右の境界を指定します。
正順のジェネレータでは、最初の値としてジェネレータが生成するのは start番目の要素であり、end番目の要素の直前の要素が最後に生成する 値となります。逆順のジェネレータでは、最初の値はend番目の要素の すぐ隣の要素であり、最後の値がstart番目の要素となります。
(generator->list (vector->generator '#(a b c d e) 2)) ⇒ (c d e) (generator->list (vector->generator '#(a b c d e) 2 4)) ⇒ (c d) (generator->list (reverse-vector->generator '#(a b c d e) 2)) ⇒ (e d c b) (generator->list (reverse-vector->generator '#(a b c d e) 2 4)) ⇒ (d c) (generator->list (reverse-vector->generator '#(a b c d e) #f 2)) ⇒ (b a)
{gauche.generator
}
これらの手続きは正確な整数を引数に取り、
bits->list
と同様に(scheme.bitwise
- R7RSビット演算参照)、
それを真偽値のシーケンス(0が偽、1を真)として扱います。
bits->generator
はLSBから、reverse-bits->generator
は
MSBからビットを取り出します。
(generator->list (bits->generator #b10110)) ⇒ (#f #t #t #f #t) (generator->list (reverse-bits->generator #b10110)) ⇒ (#t #f #t #t #f)
省略可能引数startとendはビットフィールドの範囲を指定します
(LSBが0)。list->generator
等の同名の引数と違って、
startが右端(含まれる)、endが左端(含まれない)を指定します。
この指定方法は整数のビットフィールドにアクセスする他の手続きと一貫しています
(scheme.bitwise
- R7RSビット演算参照)。
(generator->list (bits->generator #x56 0 4) ⇒ (#f #t #t #f) ; takes bit 0, 1, 2 and 3 (generator->list (bits->generator #x56 4 8) ⇒ (#t #f #t #f) ; takes bit 4, 5, 6 and 7 (generator->list (reverse-bits->generator #x56 4 8) ⇒ (#f #t #f #t) ; takes bit 7, 6, 5 and 4
註: SRFI-151のmake-bitwise-generator
は
bits->generator
と似ていますが、生成されるジェネレータは無限ジェネレータになります。
scheme.bitwise
- R7RSビット演算参照。
{gauche.generator
}
それぞれ文字、バイトを与えられた入力ポートから読み取るジェネレータを
返します。つまり、それぞれ
(cut read input-port)
、
(cut read-line input-port)
、
(cut read-char input-port)
、
(cut read-byte input-port)
と同じですが、完全性のために提供されています。
{gauche.generator
}
任意のコレクションobjを、objをたどるジェネレータに変換する
ジェネリック関数版です。さらに、objが入力ポートである場合は、
port->char-generator
を呼びます。
{gauche.generator
}
ファイルfilenameをオープンし、入力ポートを引数に取る手続き
readerによってそのファイルから読み取るジェネレータを返す。
引数open-argsは、open-input-file
に渡されます
(ファイルポート参照)。
ファイルは、ジェネレータが尽きたところで閉じられます。最後
まで読み取る前にジェネレータが破棄された場合は、そのジェネレータ
がガベージコレクタに回収されるまでファイルは開かれたままです。
特定の時点までにファイルが確実にクローズしたい場合は、with-input-from-file
の動的なエクステントの中で、リーダー手続きをジェネレータとして
使うのが良いでしょう。
{gauche.generator
}
ファイルfilenameからそれぞれS式、文字、行、バイトの列を読むジェネ
レータを返します。これらは、file->generator
のreader引数にread
、
read-char
、read-line
、read-byte
を渡して特定化したものです。
file->generator
同様、open-argsはそのままopen-input-file
に
引き渡されます(ファイルポート参照)。ジェネレータが使い切られると、
ファイルは閉じられます。
{gauche.generator
}
unfoldに似たジェネレータの構築子です (scheme.list
- R7RSリスト参照)。
Pはシード値を引数に取り、いつ止まるかを決める述語です。 Fはシード値から値を計算する手続きです。Gは現在の シード値から次のシード値を計算する手続きです。Tail-gen は最後のシード値を取り、残りを生成するジェネレータを返します。
この関数から返されたジェネレータが呼ばれるたびに、pが現在の
シード値とともに呼ばれます。これが真を返せば、やるべき事が終わっ
たことがわかり、(もし与えられていたなら)tail-gen
が残りを
生成するジェネレータを得るために呼ばれます。さもなければ、生成する
値を得るために現在のシード値にfが適用され、シード値を更新
するためにgが使われます。
(generator->list (gunfold (^s (> s 5)) (^s (* s 2)) (^s (+ s 1)) 0)) ⇒ '(0 2 4 6 8 10)
{gauche.generator
}
要素が、一つ前の要素にfを適用して計算されるような値の列を無限に生成する
ジェネレータを返します。giterate
では最初の値はseedそのものですが、
giterate1
では(f seed)
が最初の値になります。
(generator->list (giterate (pa$ * 2) 1) 10) ⇒ (1 2 4 8 16 32 64 128 256 512) (generator->list (giterate1 (pa$ * 2) 1) 10) ⇒ (2 4 8 16 32 64 128 256 512 1024)
giterate1
を別に用意しているのは、それが極めて速いからです
(giterate
に比べても10%ほど速いです)。
gauche.lazy
のliterate
も参照
(gauche.lazy
- 遅延シーケンスユーティリティ)。
[SRFI-158]{gauche.generator
}
item … を返すジェネレータを作成します。
[SRFI-158]{gauche.generator
}
giota
とほぼ同じですが、count引数は必須です。
[SRFI-158]{gauche.generator
}
grange
と同じです。
[SRFI-158]{gauche.generator
}
generate
と同じです。
[SRFI-158]{gauche.generator
}
コレクションobjと、それを一要素づつ処理するfor-each手続きから、
コレクションの要素を一つづつ生成するジェネレータを作って返します。
次の定義を考えると良いでしょう。
(define (make-for-each-generator for-each coll) (generate (^[yield] (for-each yield coll))))
作られたジェネレータが全ての値を取り出す前にobjが変更された場合の振る舞いは、 渡されたfor-each手続きがそのケースをどのように処理するかに依存します。 それは安全かもしれないし、安全でないかもしれません。一般的に、ジェネレータが EOFを返す前にobjを変更することは避けるのが賢明です。 ジェネレータがEOFを返した後にobjを変更するのは安全です。
[SRFI-158]{gauche.generator
}
省略可能なtail-gen引数を取らないことを除けば、
gunfold
と同じです。
以下に挙げる手続きは、どれもジェネレータ(genやgen2と記述されて います)を受け取ってジェネレータを返します。便宜上、これらの手続きは genやgen2としてコレクションも受けつけます;ジェネレータが 想定されているところにコレクションが渡されると、暗黙のうちにジェネレータ へと変換されるのです。
(註:これはGauche独自の拡張です。ポータブルなSRFI-121/158プログラムは、 この振る舞いに依存してはいけません。明示的にコレクションをジェネレータに 変換してください。)
[SRFI-158]{gauche.generator
}
genの前にitemを追加するジェネレータを返します。
(generator->list (gcons* 'a 'b (giota 2))) ⇒ (a b 0 1)
[SRFI-158]{gauche.generator
}
最初に与えたジェネレータが生成する値を生成し、それが尽きたら2番目に与えた
ジェネレータが生成する値を生成し、という具合に、与えられたジェネレータが
生成する値を順番に生成するジェネレータを返します。
(generator->list (gappend (giota 3) (giota 2))) ⇒ (0 1 2 0 1) (generator->list (gappend)) ⇒ ()
{gauche.generator
}
gen引数は、ジェネレータやシーケンスを生成するジェネレータです。
この関数は、genが生成する最初のジェネレータ/シーケンスの要素を次々に
生成し、それが尽きたら二番目のジェネレータ/シーケンスの要素を次々に生成し…
というジェネレータを作って返します。
(apply gappend (generator->list gen))
と似た動作ですが、
gconcatenate
はgenが無限ジェネレータであっても動作するという
利点があります。
($ generator->list $ gconcatenate $ list->generator `(,(giota 3) ,(giota 2))) ⇒ (0 1 2 0 1)
[SRFI-158]{gauche.generator
}
引数genはリストを生成するジェネレータです。
この手続きは、入力が生成するリストの各要素をひとつづつ生成するようなジェネレータを
作成して返します。
例: ゲームのテトリスは、次に落ちてくるピース (テトリミノ) を次の アルゴリズムで決めています: 各種類(O, I, T, S, Z, L, J)のテトリミノが 一つづつ入った袋をとり、そこからランダムに一つづつ取り出す。袋が空になったら 新たにテトリミノの袋をとり繰り返す。このアルゴリズムは次に示すとおり ジェネレータのパイプラインで実装できます。(テトリスはThe Tetris Companyの 登録商標です)
(use gauche.generator) (use data.random) ; for permutations-of (define g ($ gflatten $ permutations-of $ (circular-generator '(O I T S Z L J)))) (generator->list g 21) ⇒ (L O Z T J S I J L Z T I O S T L Z S I J O)
この例と、上ののgconcatenate
の例を比べてみてください。微妙な違いが
あります。gconcatenate
は、ジェネレータを生成するジェネレータを取りますが、
gflatten
はリストを生成するジェネレータを取ります。
Haskell風の型表記を使うと、これら似た手続きの違いをわかりやすく整理することができるでしょう:
gappend :: (Generator a, Generator a, ...) -> Generator a (pa$ apply gappend) :: [(Generator a)] -> Generator a gconcatenate :: Generator Generator a -> Generator a gflatten :: Generator [a] -> Generator a
[SRFI-158]{gauche.generator
}
入力ジェネレータから生成される要素を、手続きless-than
で決められる順
に生成するジェネレータを作って返します。
less-than
は入力ジェネレータの二つの要素a
, b
に対して
呼び出され、a
がb
に先行すべき時のみ#t
を返します。
入力のジェネレータはそれぞれが要素を正しい順で生成しなければなりません。 そうでない場合、出力が正しい順になっていることは保証されません。
入力が一つだけ渡された場合は、(それをジェネレータへと型変換した後で)それがそのまま
返され、less-than
は呼ばれません。
(generator->list (gmerge < '(1 3 8) '(5) '(2 4))) ⇒ '(1 2 3 4 5 8)
[SRFI-158]{gauche.generator
}
与えられたジェネレータから得られる値にprocを適用して得られる値を
生成するジェネレータを返します。返り値となるジェネレータは、引数として
与えられたジェネレータのどれかが尽きたら終了します。
注意: この手続きは、generator-map
(生成された値の畳み込み参照)
とは違います。generator-map
は一度に全ての値を消費し、結果をリストとして返しますが、
gmap
はすぐには入力を消費せずにジェネレータを返すのです。
{gauche.generator
}
状態を持つマッピングを行うmap-accum
(see コレクションに対するマッピング)のジェネレータ版
です。
引数procは、入力となるジェネレータの個数プラス1個の引数を取る
手続きで、(proc v v2 … seed)
のように呼ばれます。
v
, v2
, … は、入力ジェネレータが生成する値であり、
seedは現在のシード値です。この手続きは2つの値を返さなければい
けません。生成する値と、次のシード値です。
註:これはSRFI-121のgcombine
と同じものです。
[SRFI-158]{gauche.generator
}
gmap-accum
の別名です。SRFI-121との互換性のため提供されています。
[SRFI-158]{gauche.generator
}
ソースジェネレータgenが生成する値のうち、
predがfalseを返すもの(gfilter
の場合)、
またはpredが真の値を返すもの(gremove
の場合)を
除いた値を生成するジェネレータを返します。
(generator->list (gfilter odd? (grange 0)) 6) ⇒ (1 3 5 7 9 11) (generator->list (gremove odd? (grange 0)) 6) ⇒ (0 2 4 6 8 10)
[SRFI-158]{gauche.generator
}
ソースジェネレータgenが生成する値のうち、itemと等しいものを
取り除いた値を生成するジェネレータを返します。
比較は省略可能引数=に渡す手続きで行われ、省略時にはequal?
が使われます。
;; Note: This example relies on auto-coercing list to generator. ;; SRFI-121 requires list->generator for the second argument. (generator->list (gdelete 3 '(1 2 3 4 3 2 1))) ⇒ (1 2 4 2 1)
[SRFI-158]{gauche.generator
}
ソースジェネレータgenが生成する値を生成するジェネレータを返します。
但し、連続して等しいものはそのうち1つだけが生成されます。
比較は省略可能引数=に渡す手続きで行われ、省略時にはequal?
が使われます。
;; Note: This example relies on auto-coercing list to generator. ;; SRFI-121 requires string->generator for the second argument. (generator->list (gdelete-neighbor-dups "mississippi")) ⇒ (#\m #\i #\s #\i #\s #\i #\p #\i)
[SRFI-158]{gauche.generator
}
(gfilter values (gmap proc gen gen2 …))
と同様に動作しますが、若干効率的です。
[SRFI-158]{gauche.generator
}
この手続きは、一連の値に対するステートフルなフィルタリングを可能にします。
引数procはソースジェネレータからの値vと、シード値を取る手続きでなければ
なりません。この手続きが真を返す場合に、返り値となるジェネレータは
vを生成します。そうでない場合、ジェネレータはprocが真を返すか、ソース
ジェネレータが尽きるまで、シード値を更新しながらprocを呼び出し続けます。
次に挙げる例は、振動する値を生成するジェネレータを受け取り、直前の値よりも 大きな値のみを生成するジェネレータを返します。
(generator->list (gstate-filter (^[v s] (values (< s v) v)) 0 (list->generator '(1 2 3 2 1 0 1 2 3 2 1 0 1 2 3)))) ⇒ (1 2 3 1 2 3 1 2 3)
{gauche.generator
}
この手続きは入力と出力がn対mで対応するようなフィルタを作ります。つまり、
入力のいくつかを見て、それに応じて1個以上の出力を生成するようなフィルタです。
手続きprocは、次の入力およびシード値を受け取り、 二つの値を返します:出力値のリスト、及び次のシード値です。 出力を決定するためにもっと入力を読むことが必要なら、 第一の返り値を()にします。
入力が終端に達したら、手続きtail-genが、その時点でのシード値を引数として 呼び出されます。tail-genは出力の終端となる値のリストを返します。 tail-genが省略された場合は、入力がなくなった時点で最後のprocが 返した出力のリストが終端となります(最後のシード値は捨てられます)
例えば、テキストファイルがあり、各行にコマンドがかかれているとしましょう。 ただし行がバックスラッシュで終わった場合、次の行へと継続しているものとみなします。 以下のコードは、入力ファイルの各行を読むジェネレータから、 論理行(継続行をくっつけたもの)をひとつづつ返すジェネレータを生成します。
(gbuffer-filter (^[v s] (if-let1 m (#/\\$/ v) (values '() (cons (m 'before) s)) (values `(,(string-concatenate-reverse (cons v s))) '()))) '() (file->line-generator "input-file.txt") (^[s] `(,(string-concatenate-reverse s))))
[SRFI-158]{gauche.generator
}
それぞれ、ソースジェネレータgenが生成する値のうち最初からk個の値を生成する、
および最初からk個の値を除いた次の値から生成するようなジェネレータを
作成して返します。
これらの手続きはソースジェネレータがk個の値を生成する前に尽きたとしても、
何も文句を言いません。デフォルトでは、gtake
が返すジェネレータはソース
ジェネレータが終端に達した時点で終了します。しかし、省略可能引数padding
を与えた
場合、返されるジェネレータは足りない分をpadding
で補うことで
常にk個の値を生成します。
註: fill引数を渡した場合、たとえ入力ジェネレータが終端に達していたとしても
gtake
が返すジェネレータはk個の値を生成します。つまりその場合、
入力が使い尽くされたかどうかを判定する汎用的な方法はありません。
「入力が尽きるまでk個の要素を次々に取り出したい」という場合は
下に説明するgslices
の方が使い勝手が良いかもしれません。
互換性への註: 0.9.4までは、gtake
はfill?とpaddingの
ふたつの省略可能引数を取りました。これはもともとGauche組み込みtake*
に合わせて
いたのですが、SRFI-121に採用されたgtake
とは非互換になりました。
SRFI-121の方が簡潔で直感的なので、0.9.5からは元のgtake
を(take*
との
類似性を強調して)gtake*
とリネームし、gtake
はSRFI-121に合わせることと
しました。
移行をスムースにするため、gtake
は二つの省略可能引数(合計4つの引数)を取ることを
許します。その場合、gtake*
が呼ばれたのと同じ動作になります。
従って、gtake
に4引数を渡している従来のコードは、0.9.5以前でも以降でも
動作します。
{gauche.generator
}
gtake
のバリエーションで、一つの省略可能引数paddingのかわりに、
take*
と同じように二つの省略可能引数を取ります
(リストへのアクセスと変更参照)。
0.9.4まではこれがgtake
と呼ばれていました。
互換性のために名前を変えて残してあります。
[SRFI-158]{gauche.generator
}
ジェネレータ版の take-while
と drop-while
(リストへのアクセスと変更参照) です。
gtake-while
が返すジェネレータは、ソースジェネレータが生成する値に対して
predが真を返す限り、その値を生成します。gdrop-while
が返す
ジェネレータは、まずソースジェネレータから値を読み取り、その値に対して
predが真を返したら、値の生成を開始します。
{gauche.generator
}
slices
のジェネレータ版です (リストへのアクセスと変更参照。
入力のジェネレータgenからk要素づつ読み出して、そのリストを生成する
ジェネレータを返します。
(generator->list (gslices (giota 7) 3)) ⇒ ((0 1 2) (3 4 5) (6))
fill?引数とpadding引数は、gtake
のそれと同様に、
入力が足りなかった場合の処理を指定します。デフォルトの、fill?が
#f
の場合、入力がk要素に満たなければ、出力の要素も
切り詰められます (上の例参照)。fillが真の値であれば、
入力が足りない分はpaddingが当てられ、出力の最後のリストもk要素になります。
(generator->list (gslices (giota 6) 3 #t 'x)) ⇒ ((0 1 2) (3 4 5)) (generator->list (gslices (giota 7) 3 #t 'x)) ⇒ ((0 1 2) (3 4 5) (6 x x))
[SRFI-158]{gauche.generator
}
genからk要素づつ取って作ったリストを次々と生成するジェネレータを返します。
paddingが省略された時は(gslices gen k)
と同じ、
paddingが与えられた時は(gslices gen k #t padding)
と同じになります。
この手続きはSRFI-158で定義されたので、gslices
よりポータブルでしょう。
{gauche.generator
}
genは暗黙の変換後、文字を生成するジェネレータでなければなりません。
この手続きが返すジェネレータは、genが生成する文字のシーケンスに対して、
regexpをマッチさせようとする。そして一度マッチすると、その位置を記憶して
#<rxmatch>
オブジェクトを返します。さらにマッチすることがなければ、
ジェネレータは尽きます。
($ generator->list $ gmap rxmatch-substring $ grxmatch #/\w+/ "The quick brown fox jumps over the lazy dog.") ⇒ ("The" "quick" "brown" "fox" "jumps" "over" "the" "lazy" "dog")
注意: この手続きはgenが文字列の場合、ジェネレータへの変換がバイパスされるため 効率的です。genが文字列ではない場合、現在の実装では、genが生成する文字の シーケンス全体の長さがnの場合に、係数が小さいとはいえ、必要となるregexpの適用 回数は、O(n^2)となるかもしれません。この点について、将来改良されるかもしれませんが、 大きな入力に対してこの関数を使う場合は、注意してください。
もう一点注意: genが文字列でない場合、rxmatchはバッファリングされた
部分的な入力に対して適用されます。このため、返されたマッチのrxmatch-after
は、マッチ後の“入力の残り全部”を表現しません。単にバッファの中にある文字列の
残りになります。
[SRFI-158]{gauche.generator
}
引数はどちらもジェネレータです。igenは単調増加する正確な非負整数を
生成しなければなりません。
vgenから生成される値のうち、igenからの数字をインデックスとする 値のみを取り出して返すようなジェネレータを作成して返します。 どちらかの入力ジェネレータが終端に達したら、返されるジェネレータも終端に達します。
igenが条件を満たさない値を生成した場合はその時点でエラーが投げられます。
;; This example takes advantage of Gauche's auto-coercing ;; list to generator. For portable SRFI-121 programs, ;; you need list->generator for each argument: (generator->list (gindex '(a b c d e) '(0 2 3))) ⇒ (a c d)
[SRFI-158]{gauche.generator
}
引数はどちらもジェネレータです。vgenからの要素のうち、
対応するbgenの要素が真の値であるものだけを選んで返す
ようなジェネレータを作成して返します。
ソースジェネレータの一つが終端に達した時に、作成された ジェネレータも終端に達します。
;; This example takes advantage of Gauche's auto-coercing ;; list to generator. For portable SRFI-121 programs, ;; you need list->generator for each argument: (generator->list (gselect '(a b c d e) '(#t #t #f #t #f))) ⇒ (a b d)
ビットジェネレータと一緒に使うと、gselect
でビットマスク
により要素を抽出することができます。
(generator->list (gselect '(a b c d e) (reverse-bits->generator #x1a))) ⇒ (a b d)
いくつかのジェネレータ消費手続きは組み込みになっています。
generator-fold
、generator-fold-right
、 generator-for-each
、
generator-map
、generator-find
については、
生成された値の畳み込みを参照してください。
[SRFI-158]{gauche.generator
}
generatorから項目を読み取り、それらを要素とするリストを返します
(generator->reverse-list
では要素が逆順になります)。
デフォルトでは、ジェネレータを使い切るまで読み取ります。省略可能
引数kを与える場合、それは非負整数でなければならず、結果のリスト
は、k個の項目を読み取るか、ジェネレータを使い切ったところで終わり
となります。
無限ジェネレータを渡す時は必ずkを指定しましょう。 さもなくば、この手続きは制御を返さず、全てのメモリを食い尽して クラッシュするでしょう。
[SRFI-158]{gauche.generator
}
procは与えられたジェネレータの数と同じだけの引数を取る手続きです。
gen gen2 …から要素をひとつづつ取ってそれらにprocを 適用した結果のリストを作って返します。 どれかのジェネレータが終端に達したらリストも終わります。
リストは積極的に作られます。全てのジェネレータが無限だと、この手続きは戻ってきません。
[SRFI-158]{gauche.generator
}
genから、k要素もしくはgenが終端に達するまで要素を読み出し、
それらの要素からなるベクタまたは文字列を作って返します。
kが省略された場合はgenが終端に達するまで呼ばれますが、 genが無限ジェネレータだと返って来なくなるので注意してください。
generator->string
の場合、genが文字以外のものを生成したら
エラーが報告されます。
[SRFI-158]{gauche.generator
}
ジェネレータgenから、k個の要素、あるいはgenが終端に達するまで
要素を読み出し、それらの要素からなるユニフォームベクタを作って返します。
kが省略された場合、genは常に最後まで読まれます。
classが指定された場合、それはユニフォームベクタのクラスのいずれかで
なければなりません(ユニフォームベクタ参照)。省略された場合は
<u8vector>
が使われます。
generator->bytevector
は、classが<u8vector>
に固定されている
以外はgenerator->uvector
と同じです。
ジェネレータgenは、指定されたユニフォームベクタの要素となり得る数値を 生成しなければなりません。それ以外の値が生成された場合はエラーが報告されます。
[SRFI-158]{gauche.generator
}
vectorを、インデックスatから、genが生成する値によって
埋めてゆきます。genが終端に達するか、ベクタの最後まで埋められた時点で
終了し、生成された要素の個数が返されます。
(define v (vector 'a 'b 'c 'd 'e)) (generator->vector! v 2 (giota)) ⇒ 3 v ⇒ #(a b 0 1 2)
{gauche.generator
}
generator->vector!
と同様、genから読み出した値で、
uvectorを、インデックスatから順に埋めてゆきます。
ベクタの終端に達するか、genが終端に達するまで続けられ、
読み出された要素の個数が戻り値となります。
generator->uvector!
はどんなユニフォームベクタでも取ることができます。
generator->bytevector!
はu8vector
限定です。
ジェネレータgenは、指定されたユニフォームベクタの要素となり得る数値を 生成しなければなりません。それ以外の値が生成された場合はエラーが報告されます。
{gauche.generator
}
これは、ジェネレータコードに頻繁に現れるモナド的なパターンを表現
するものです。and-let*
の発想に似ていますが、#f
のかわりにEOFを
返す式を評価すると直ちに返ります。
binding部分は(var expr)
か( expr )
の形をとります。
実際の定義を見れば、この構文が明解に理解できるでしょう。
(define-syntax glet* (syntax-rules () [(_ () body body2 ...) (begin body body2 ...)] [(_ ([var gen-expr] more-bindings ...) . body) (let1 var gen-expr (if (eof-object? var) var (glet* (more-bindings ...) . body)))] [(_ ([ gen-expr ] more-bindings ...) . body) (let1 var gen-expr (if (eof-object? var) var (glet* (more-bindings ...) . body)))]))
{gauche.generator
}
これとglet*
の関係は、let1
とlet*
のそれと同じです。言葉を
変えれば、これは(glet* ([var expr]) body body2 …)
と同じです。
{gauche.generator
}
これは、ジェネレータ版のdolist
でありdotimes
です(変数束縛参照)。
gexprはジェネレータを生成する式であり、一度だけ評価されます。 結果となるジェネレータはEOFを返すまで繰り返し呼ばれます。ジェネレータ が呼ばれる毎に、ジェネレータが生成する値に束縛されるvarの スコープ内でbody …が評価されます。
dolist
やdotimes
がそうであるように、このマクロも副作用のため
のものです。同じことはfor-each
族を使っても書けますが、このマクロを
使って命令的に記述したコードの方が読みやすいこともあるのです。
(do-generator [line (file->line-generator "filename")] ;; lineを使って何か副作用のある作業をする )
[SRFI-158]{gauche.generator
}
any
、every
(see リストをたどる手続き) と同じですが、
ジェネレータに対して使えます。
[SRFI-158]{gauche.generator
}
genが生成する要素ののうち、predを満たすものの個数を返します。
副作用としてgenは使い尽くされます。
[SRFI-158]{gauche.generator
}
genが生成する値をseedとしてunfoldを適用します。次の式と同じです:
(unfold eof-object? identity (^_ (gen)) (gen) arg …)
unfold手続きは、
scheme.list
モジュールのunfold
(see scheme.list
- R7RSリスト参照)のように、
次のとおり引数を取らなければなりません:
(unfold stop? mapper successor seed arg …)
。
これはジェネレータをシーケンスに変換する一般的な方法と見ることができます。
x->generator
の反対です。
(generator-unfold (x->generator "abc") string-unfold) ⇒ "abc"