我是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