我是Scheme(通過球拍)和(在較小程度上)函數式編程的新手,並且可以通過變量vs遞歸在積累的優缺點上使用一些建議。爲了這個例子的目的,我試圖計算一個移動平均數。所以,對於名單'(1 2 3 4 5)
,3期移動平均值將爲'(1 2 2 3 4)
。這個想法是,在該期間之前的任何數字還不是計算的一部分,並且一旦我們達到了集合中的期間長度,我們就開始根據選定的期間對列表的子集進行平均。計劃/球拍最佳實踐 - 遞歸vs可變積累
所以,我的第一次嘗試看起來是這樣的:
(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))
等
但是,它不是在方案的精神(是嗎?),因爲我有副作用的積累。所以我重寫它看起來像這樣:
(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中?
嗯,那樣更好。 :) 非常感謝。 – Scott 2012-02-01 06:38:42