私はScheme(Racket経由)と(それほどではありませんが)関数型プログラミングに不慣れで、変数と再帰による累積の長所と短所についてアドバイスを使用できます。この例では、移動平均を計算しようとしています。したがって、 list'(1 2 3 4 5)
の場合、3 期間の移動平均は になります'(1 2 2 3 4)
。アイデアは、期間の前の数値はまだ計算の一部ではなく、セットの期間の長さに達すると、選択した期間に従ってリストのサブセットの平均化を開始するというものです。
したがって、私の最初の試みは次のようになりました。
(define (avg lst)
(cond
[(null? lst) '()]
[(/ (apply + lst) (length lst))]))
(define (make-averager period)
(let ([prev '()])
(lambda (i)
(set! prev (cons i prev))
(cond
[(< (length prev) period) i]
[else (avg (take prev period))]))))
(map (make-averager 3) '(1 2 3 4 5))
> '(1 2 2 3 4)
これは機能します。そして、私は地図の使い方が好きです。構成可能で、リファクタリングが可能です。将来、次のようないとこがいることがわかります。
(map (make-bollinger 5) '(1 2 3 4 5))
(map (make-std-deviation 2) '(1 2 3 4 5))
等
しかし、Scheme の精神ではありません (そうですか?)。ということで、以下のように書き直しました。
(define (moving-average l period)
(let loop ([l l] [acc '()])
(if (null? l)
l
(let* ([acc (cons (car l) acc)]
[next
(cond
[(< (length acc) period) (car acc)]
[else (avg (take acc period))])])
(cons next (loop (cdr l) acc))))))
(moving-average '(1 2 3 4 5) 3)
> '(1 2 2 3 4)
さて、このバージョンは一見すると理解するのがより困難です。だから私はいくつかの質問があります:
ラケットの組み込み反復構造のいくつかを使用して、再帰バージョンを表現するよりエレガントな方法はあり
for/fold
ますか? 書かれているように末尾再帰ですか?アキュムレータ変数を使用せずに最初のバージョンを作成する方法はありますか?
このタイプの問題は、特にSchemeでベストプラクティスが認められているより大きなパターンの一部ですか?