Let's start Scheme

2018-01-14

リストの作成

こんなツイートをしたが、どう書くかというのは示してなかったので(iPhone上からだったので)、せっかくだしブログのネタにしようという話。



リストの生成方法にはいくつか方法があるが、今回は有名どころを二つ書いておく。
cons + reverse
cons + reverseはまぁよく使われるパターンで、この名前で呼べば大体どんなものかの想像がつくくらいだ(と思う)。ツイートで言及しているQiita記事にある手続きfをこのパターンで書き直してみた。
(define (f/cons+reverse init n)
  (let loop ((i 0) (r init))
    (if (< i n)
        (loop (+ i 1) (cons (tent_func (car r)) r))
        (reverse r))))
nthが何をしているものなのかよくわからなかったが、おそらく(nth -1 L)はリストの最終要素を取り出すものと予想。(そのように実装して元の手続きを動かしたらそれっぽい値返したし。)
これだとappendがなくなるので、O(n^2)がO(n)になる。一時リストの生成が嫌ならreverse!を使えば生成されるリストもたかだか一個になる。
doで以下のように書くこともできる。
(define (f/cons+reverse init n)
  (do ((i 0 (+ i 1)) (r init (cons (tent_func (car r)) r)))
      ((= i n) (reverse r))))
どちらがいいかは好み次第。
ちなみに、reverseがないので保留と言われた(記憶、Twitter上で見つからん)のだが、reverseはこんな風に書ける。
(define (reverse l)
  (do ((l l (cdr l)) (r '() (cons (car l) r)))
      ((null? l) r)))
RnRS準拠の処理系なら絶対持ってるはずだが、自作の処理系だったという可能性も踏まえてみる。 (積極的に無駄な処理をするコードを直していくスタイル)

unfold
Schemeの標準にはないが、SRFI-1 (R7RS-large)にはunfold という手続きがある。これを使うと上記は以下のように書ける。
(define (f/unfold init n)
  (unfold (lambda (x) (< (car x) 0))
          cdr
          (lambda (x) (cons (- (car x) 1) (tent_func (cdr x))))
          (cons n init)))
こっちは多少メモリ効率が悪い(シードの生成に必ずペアを作ってる)し、unfoldの使用方法が多少変則的な感はある。

ループ内で(append result (list ...))みたいなコードを見つけたら積極的に書き換えていきたいところである。

No comments:

Post a Comment