不考虑效率:
(defn average [lst] (/ (reduce + lst) (count lst)))
(defn moving-average [window lst] (map average (partition window 1 lst)))
user> (moving-average 5 '(1 2 3 4 5 6 7 8))
(3 4 5 6)
Run Code Online (Sandbox Code Playgroud)
如果你需要快速,那么有一些相当明显的改进!但它会变得不那么优雅.
在SO上有一个非常相似的问题:计算列表的移动平均值.它更通用 - 代表了许多FP友好语言,使用Scala接受了答案 - 但是有一些很好的Clojure解决方案.
我在那里发布了自己的解决方案.注意它确实使用lazy-seq,但那是因为我希望它在很长一段时间内表现良好(这意味着调整每一步的平均值,而不是计算每个大小=周期窗口到输入列表中的单独平均值).查看Q以获得良好的解决方案,从而进行另一种权衡,从而产生更短的代码,具有更多的声明感,实际上在非常短的时间段内表现更好(尽管在较长时期内会出现明显的减速,如预期的那样).
这个版本更快一点,特别是对于长窗口,因为它保持滚动总和并避免重复添加相同的东西。
由于lazy-seq,它也非常通用并且不会破坏堆栈
(defn partialsums [start lst]
(lazy-seq
(if-let [lst (seq lst)]
(cons start (partialsums (+ start (first lst)) (rest lst)))
(list start))))
(defn sliding-window-moving-average [window lst]
(map #(/ % window)
(let [start (apply + (take window lst))
diffseq (map - (drop window lst) lst)]
(partialsums start diffseq))))
Run Code Online (Sandbox Code Playgroud)
;; 为了帮助查看它在做什么:
(sliding-window-moving-average 5 '(1 2 3 4 5 6 7 8 9 10 11))
start = (+ 1 2 3 4 5) = 15
diffseq = - (6 7 8 9 10 11)
(1 2 3 4 5 6 7 8 9 10 11)
= (5 5 5 5 5 5)
(partialsums 15 '(5 5 5 5 5 5) ) = (15 20 25 30 35 40 45)
(map #(/ % 5) (20 25 30 35 40 45)) = (3 4 5 6 7 8 9)
Run Code Online (Sandbox Code Playgroud)
;; 例子
(take 20 (sliding-window-moving-average 5 (iterate inc 0)))
Run Code Online (Sandbox Code Playgroud)