(append L (list ...)) となっているところは cons + reverse に書き換えられる気がする。O(n^2) が O(n) になるからかなり性能に響くはず。 https://t.co/f14prELLP7— Kei (@tk_riple) 4 January 2018
リストの生成方法にはいくつか方法があるが、今回は有名どころを二つ書いておく。
【
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