вычислите среднее значение списка чисел, используя карту, и уменьшите
Там не нужно 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 (примерно в 2 раза быстрее).
Ссылки : Эффективное вычисление среднего значения списка в Haskell