順列生成ストリーム版

実は動いているのかも

昨日,ストリームを使ったアパート問題のプログラムがすんなり動かないと書いたが,実は遅いだけで,正しく動いているのかも….

とりあえず,今日は順列生成のストリーム版について書いてみる.いや,本当は順列というより,置換と言うべきなのかな.n個の異なる数からn個を選ぶのだから.

リスト版のpermutations

リスト版のpermutationsは,昨日のプログラムにもあるように,Gaucheの場合util.combinationsに含まれているけど,ストリーム版のpermutationsを作る準備として,まずはリスト版のpermutationsを作ってみる.

SICPにあるのは覚えていたが,ちゃんと書けるかどうか,力試しのつもりで見ずに書いたら,結構な時間がかかってしまった….というか,関数名のヒントなしには一週間後にパッと読む自信がないが,Schemerなら普通スラスラ読めるのだろうか?

(use srfi-1)
(define (permutations l)
  (if (null? l)
      (list '())
      (append-map (lambda (x)
                    (map (lambda (y) (cons x y))
                         (permutations (remove (lambda (z) (= z x)) l))))
                  l)))

ストリーム版のpermutations

stream-apppend-mapより,stream-foldの方が書きやすかったので,それを使う以外は,上記プログラムをほぼ機械的に変換したのが下記のストリーム版.

(use util.stream)

(define (stream-fold f seed l)
  (stream-delay
   (if (stream-null? l)
       seed
       (stream-fold f (f seed (stream-car l)) (stream-cdr l)))))

(define (stream-remove x l)
  (stream-delay (stream-filter (lambda (i) (not (= i x))) l)))

(define (stream-permutations l)
  (stream-delay
   (if (stream-null? l)
       (stream-cons stream-null stream-null)
       (stream-fold
        stream-append stream-null
        (stream-map (lambda (x)
                      (stream-map (lambda (y) (stream-cons x y))
                                  (stream-permutations (stream-remove x l))))
                    l)))))

下記のテストであまり待たされないところを見ると,どうやらうまく動いているらしい.

(define (main args)
  (let ((p (stream-permutations (stream-iota 20 1))))
    (print (stream->list (stream-ref p 50)))))

後は,stream-permutationsを決定性計算版のアパート問題プログラムに組み込めばいいはず.

ストリーム版のpermutations,本当はもとの意図と違っている

本当は,stream->listを使わなくても,(stream-ref p 50)などとすれば,順列がひとつリストとして取り出せるようなストリームが書きたかったのだが,どうやっていいのかわからない.うまい方法はあるのだろうか….それとも,こういう場合はストリームの各要素もストリームになるように作るのが定石なのだろうか….