Added average and average1 functions#318
Conversation
|
I can update CHANGELOG, but it wasn't immediately obvious if I've should've created a new hypothetical version there or not. |
chshersh
left a comment
There was a problem hiding this comment.
Looking good 👍 I have a few suggestions regarding further improvements, but shouldn't be too hard to fix, I hope 🙂
| . foldl' (\(!total, !count) x -> (total + x, count + 1)) (0,0) | ||
| | otherwise = Just | ||
| . uncurry (/) | ||
| . foldl' (\(!total, !count) x -> (total + x, count + 1)) (0,0) |
There was a problem hiding this comment.
I would rewrite this function using recursive go function with two accumulators as separate arguments. foldl' doesn't bring much here, instead, some time and space is wasted on tuple allocation, so having a tuple is redundant. It's better to write a manual simple recursive function.
There was a problem hiding this comment.
Just a quick question here. The type is (Foldable f, Fractional a) => f a -> a. Unless it goes via a toList or something, I can't really see how to manually recurse over a generic f a.
I did a casual benchmark and having bang patterns seems almost as good as just having manual recursion.
average1_h :: NonEmpty Double -> Double
average1_h (f :| ls) = go f 1 ls
where
go t c [] = t/c
go !t !c (x:xs) = go (t+x) (c+1) xs
-- vs.
average1_c :: (foldable1 f, fractional a) => f a -> a
average1_c = uncurry (/) . foldl' (\(!total, !count) !x -> (total + x, count + 1)) (0,0)
These benchmark over 100 000 elements like this:
benchmarking average1_h
time 538.7 μs (509.7 μs .. 573.3 μs)
0.967 R² (0.953 R² .. 0.980 R²)
mean 562.5 μs (537.1 μs .. 597.6 μs)
std dev 99.05 μs (74.08 μs .. 144.2 μs)
variance introduced by outliers: 91% (severely inflated)
benchmarking average1_c
time 612.3 μs (570.0 μs .. 676.1 μs)
0.948 R² (0.915 R² .. 0.984 R²)
mean 615.0 μs (590.9 μs .. 659.5 μs)
std dev 105.2 μs (73.69 μs .. 146.5 μs)
variance introduced by outliers: 91% (severely inflated)
Even the bang patterns don't really pay out with optimizations, but they are significant if you compile without -O. Things of course may change in practise. GHC is bit fickle.
There was a problem hiding this comment.
Makes sense! Thanks for exploring this area 👍
Co-authored-by: Dmitrii Kovanikov <kovanikov@gmail.com>
We can update the CHANGELOG, don't worry 🙂 But if you're willing to update it by yourself, the next version will be |
Co-authored-by: Dmitrii Kovanikov <kovanikov@gmail.com>
Resolves #316
Checklist:
HLint
hlint.dhallaccordingly to my changes (add new rules for the new imports, remove old ones, when they are outdated, etc.)..hlint.yamlfile (see this instructions).General
stylish-haskellfile.[ci skip]text to the docs-only related commit's name.