Language agnostic 使用折叠的平均值
如何使用map和reduce计算数字列表的平均值 理想情况下,我想打电话给名单上的reduce,得到一个平均值。您可以选择首先映射和过滤该列表 骨架LISP尝试:Language agnostic 使用折叠的平均值,language-agnostic,functional-programming,average,Language Agnostic,Functional Programming,Average,如何使用map和reduce计算数字列表的平均值 理想情况下,我想打电话给名单上的reduce,得到一个平均值。您可以选择首先映射和过滤该列表 骨架LISP尝试: (defun average (list) (reduce ... list)) 一次尝试: function average (array) { return array.reduce(function() { .. }, 0); } 如果你用一种语言给出一个带有实际代码的答案,请把它解释成我是该语言的初学者
(defun average (list)
(reduce ... list))
一次尝试:
function average (array) {
return array.reduce(function() {
..
}, 0);
}
如果你用一种语言给出一个带有实际代码的答案,请把它解释成我是该语言的初学者(我可能会是)
我想避免这个琐碎的回答
function average (array) {
return sum(array) / array.length;
}
它在末尾使用除法,而不是reduce语句。我认为这是“作弊”。
[[编辑]]
解决了我自己的问题。如果有人使用LISP或Haskell中的语法糖提出了一个优雅的解决方案,我会感兴趣。正如@abesto所提到的,它需要一个迭代算法
Let counter be 0
For each [value, index] in list
let sum be (counter * index) + value
let counter be sum / (index + 1)
return counter
javascript实现将是
var average = function(list) {
// returns counter
return list.reduce(function(memo, val, key) {
// memo is counter
// memo * key + val is sum. sum / index is counter, counter is returned
return ((memo * key) + val) / (key + 1)
// let counter be 0
}, 0);
}
描述Haskell中的一个apporach,它允许使用组合方法进行褶皱:
以下是common lisp的一个版本:
(defun running-avg (r v)
(let* ((avg (car r))
(weight (cdr r))
(new-weight (1+ weight)))
(cons (/ (+ (* avg weight) v) new-weight) new-weight)))
(car (reduce 'running-avg '(3 6 5 7 9) :initial-value '(0 . 0)))
;; evaluates to 6
它跟踪运行平均值和重量,并将新的平均值计算为((以前的平均值*重量)+新值)
使用map和reduce计算数字列表的平均值
不需要map
。只需展开以生成列表,并折叠以将其减少到平均值:
mean n m = uncurry (/) . foldr g (0, 0) . unfoldr f $ n
where
f b | b > m = Nothing
| otherwise = Just (b, b+1)
g x (s,n) = (s+x, n+1)
有效的实施
这种结构(fold.unfold
)允许进行融合优化。一个特别有效的实现将融合展开(列表生成)和折叠(列表缩减)。这里,在Haskell中,GHC通过流融合将两个阶段(展开==enumFromN
)和折叠结合起来:
import Data.Vector.Unboxed
data Pair = Pair !Int !Double
mean :: Vector Double -> Double
mean xs = s / fromIntegral n
where
Pair n s = foldl' k (Pair 0 0) xs
k (Pair n s) x = Pair (n+1) (s+x)
main = print (mean $ enumFromN 1 (10^7))
编译器将其从两个函数的组合转换为递归循环:
main_loop a d e n =
case ># a 0 of
False -> (# I# n, D# e #);
True -> main_loop (-# a 1) (+## d 1.0) (+## e d) (+# n 1)
这将在汇编(编译器的C后端)中简化为此goto
:
LLVM带来了更高效但更混乱的实现(大约快2倍)
参考文献:Mathematica中的
mean[l_List]:=
Fold[{First@#1+1,(#2 +#1[[2]](First@#1-1))/First@#1}&,{1,1},l][[2]]
In[23]:= mean[{a,b,c}]
Out[23]= 1/3 (a+b+c)
也许更适合CodeGolf.SE?一个简单的
reduce
akafold
不是mapreduce
(如谷歌框架)。即使你事先把输入映射到地图上也不行。@delnan对不起,我把术语弄糊涂了。第一步应该是算出解决方案背后的数学。你能想出一个迭代算法来满足你的需求吗?将其转换为一个折叠应该不会太难。@abesto解决方案现在看起来很简单。我没想谢谢你。你能解释一下1+
和weight
吗。我对LISP一无所知。另外,*(carr r)(cdr r)
是r
元组吗?1+
是一个向参数添加1的函数weight
只是一个局部变量名。是的,r
是一个元组;通过创建一些有意义的名称,我对它进行了一些清理。这是:initial value
所做的,但您能解释一下该语法的语义吗?它是reduce
的可选关键字参数。通用Lisp函数可以采用命名参数,其中名称是Lisp符号。前导冒号只是符号的lisp语法。对于初始值,函数的第一次调用是使用(initial value,element0)
,而不是(element0,element1)
。现在我理解了代码,我更喜欢原始版本,因为它具有经典的LISP简洁性:)如果我去“Haskell for初学者课程”,那篇文章会有一些意义“漂亮的折叠式”帖子更容易理解。
mean[l_List]:=
Fold[{First@#1+1,(#2 +#1[[2]](First@#1-1))/First@#1}&,{1,1},l][[2]]
In[23]:= mean[{a,b,c}]
Out[23]= 1/3 (a+b+c)