计算使用地图号码列表的平均值,并减少
有没有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)
这减少了这个goto
在组件(该C后端到编译器):
Main_mainzuzdszdwfoldlMzqzuloop_info:
leaq 32(%r12), %rax
cmpq %rax, 144(%r13)
movq %r12, %rdx
movq %rax, %r12
jb .L198
testq %r14, %r14
jle .L202
.L199:
movapd %xmm5, %xmm0
leaq -1(%r14), %r14
movsd .LC0(%rip), %xmm5
addq $1, %rsi
addsd %xmm0, %xmm6
movq %rdx, %r12
addsd %xmm0, %xmm5
jmp Main_mainzuzdszdwfoldlMzqzuloop_info
更有效,但更混乱实现资源LLVM的速度(约快两倍)。
参考:Computing the mean of a list efficiently in Haskell
更好的适合CodeGolf.SE吧? – ircmaxell 2011-05-12 20:15:05
一个简单的'reduce'又名'fold'不是'mapreduce'(就像谷歌框架一样)。即使你事先映射输入。 – delnan 2011-05-12 20:18:37
@delnan对不起,我弄糊涂了我的术语。 – Raynos 2011-05-12 20:19:37