2007/02/17

連番を作りたい。ようするに、こんな動作をするプロシージャintegがほしい。
gosh> (list (integ) (integ) (integ))
(1 2 3)
まあ、グローバル変数を破壊的に更新すればいい。
(define n 0)
(define (integ)
(set! n (+ n 1))
n)
でもそんなSchemeコードはいやだ。主に気分的な理由で。こういう問題にはcall/ccを使うのがオレブーム。
(define (integ)
(let R ((n 0))
(call/cc
(lambda (k)
(set! integ (lambda () (R (+ n 1))))
(+ n 1)))))
しかしこれではcall/ccの意味がまったくありませんでした。すみません。以下で十分です。
(define (integ)
(let R ((n 0))
(call/cc
(lambda (k)
(set! integ (lambda () (R (+ n 1))))
(+ n 1)))))

気を取り直して。応用。
(let R ((str "hello hello hello hello"))
(rxmatch-if (rxmatch #/ / str)
(space)
(R (regexp-replace space str #`",(integ)"))
str))

=> hello1hello2hello3hello"
Gaucheに用意されているregexp-replace-allという便利な関数と組み合わせると編集者にとってはプチよろこばしい。
gosh> (regexp-replace-all #/ / "hello hello hello hello" (lambda (m) (integ)))
"hello4hello5hello6hello"

ところでintegを次のように定義するとうまくいかない理由を昨日から考えているんだけど、わからない。これでもとくに問題なさそうなんだけど、(list (integ) (integ)) のように実行しても1つめの(integ)が評価されるだけ。
(define (integ)
(call/cc
(lambda (skip)
(let R ((n 0))
(call/cc
(lambda (k)
(set! integ (lambda () (k '())))
(skip (+ n 1))))
(R (+ n 1))))))

4 件のコメント:

匿名 さんのコメント...

それ call/cc 関係ないんちゃいます?
(define (integ)
(let R ((n 0))
(set! integ (lambda () (R (+ n 1))))
(+ n 1)))

すなおなのは
(define integ
(let ((n 0))
(lambda () (set! n (+ n 1)) n)))

「ところで」の方は, いきなり (list (integ) (integ)) を評価すると無限ループ?
定義後まず (integ) を一度やっておけば数字(リストではない!)を順次返しますね.

k16 さんのコメント...

あ、確かに k を使ってさえいない!
最初に「ところで」のほうをずっといじっていたせいで、自分が何をしていたのか見失っていました。本文も訂正しておきます。

「ところで」のほうは、どうしてそういう動作になるのか、以前としてよく分かっていません。

> 定義後まず (integ) を一度やっておけば数字(リストではない!)を順次返しますね.

リストでないだけでなく、(integ)を1回しか実行しないのが謎です。

gosh> (integ)
1
gosh> (list (integ) (integ))
2
gosh> (list (integ) (integ))
3

4が返ってきてもよさそうなもの?

匿名 さんのコメント...

ふむ.
gosh> (* (integ) 3)
3
gosh> (list (integ) (integ))
6
gosh> (list (integ) (integ))
9

最初に (integ) が呼ばれたときに integ をすりかえる.
何にすりかえるかというと, 「継続 k に '() を渡す」という関数に.
上の例の場合, 継続 k とは「(R (+ n 1)) を求めてその結果を 3 倍する」.

(list (integ) (integ)) で起きているのは…
まず片方の (integ) を評価しようとする.
この時点での継続 k0 は,
「もう片方の (integ) も評価し, 両者の結果を要素とするリストを作る」.
でも (integ) は上に書いたとおりなので, k0 を放棄して k の方へ.

k16 さんのコメント...

ありがとうございます。ようやく何を勘違いしていたのか見えてきました。

> この時点での継続 k0 は,
> 「もう片方の (integ) も評価し, 両者の結果を要素とするリストを作る」.

「(list (integ) (integ))という文脈で最初の(integ)がとらえる継続」がまったく頭から落ちていました。
個々の(integ)は別々の環境で評価されて、listはその結果をそれぞれ使うとでも思い込んでいたようです。

# にしても、納得してからこうやって文章にしてみると、あからさまに「んなわけないじゃん」という話ですね……