<div><div>If you want a much more performant solution, go around the list once. Another advantage is that it will run in constant memory.<br></div><div><br></div><div>```<br>-- requires BangPatterns extension; bonus questions: <br>-- * Can you predict what will happen with out bang patterns? <br>-- * Would `foldl'` work even without any bang patterns?<br></div></div><div>mean5 :: [Double] -> Double<br></div><div>mean5 = snd . foldl (\(!n, !r) a -> (n + 1, a / n + (n - 1) / n * r)) (1, 0)<br></div><div></div><div><div>```<br></div><div><br></div><div><br></div><div>`mean5` on my system reports:<br></div><div><div>```<br></div> 1,840,101,632 bytes allocated in the heap<br></div></div><div> 197,896 bytes copied during GC<br></div><div> 44,408 bytes maximum residency (2 sample(s))<br></div><div> 29,320 bytes maximum slop<br></div><div> 2 MiB total memory in use (0 MB lost due to fragmentation)<br></div><div><br></div><div> Tot time (elapsed) Avg pause Max pause<br></div><div> Gen 0 1774 colls, 0 par 0.005s 0.006s 0.0000s 0.0000s<br></div><div> Gen 1 2 colls, 0 par 0.000s 0.000s 0.0001s 0.0001s<br></div><div><br></div><div> INIT time 0.000s ( 0.000s elapsed)<br></div><div> MUT time 0.236s ( 0.235s elapsed)<br></div><div> GC time 0.005s ( 0.006s elapsed)<br></div><div> EXIT time 0.000s ( 0.000s elapsed)<br></div><div> Total time 0.241s ( 0.241s elapsed)<br></div><div><br></div><div> %GC time 0.0% (0.0% elapsed)<br></div><div><br></div><div> Alloc rate 7,803,840,935 bytes per MUT second<br></div><div><br></div><div> Productivity 97.9% of total user, 97.5% of total elapsed<br></div><div></div><div><div>```<br></div><div><br></div><div>while `mean`<br></div><div>```<br></div><div> 1,280,101,688 bytes allocated in the heap<br></div><div> 1,099,489,280 bytes copied during GC<br></div></div><div> 294,937,752 bytes maximum residency (12 sample(s))<br></div><div> 50,518,888 bytes maximum slop<br></div><div> 670 MiB total memory in use (0 MB lost due to fragmentation)<br></div><div><br></div><div> Tot time (elapsed) Avg pause Max pause<br></div><div> Gen 0 1209 colls, 0 par 0.379s 0.381s 0.0003s 0.0012s<br></div><div> Gen 1 12 colls, 0 par 0.976s 0.976s 0.0813s 0.3921s<br></div><div><br></div><div> INIT time 0.000s ( 0.000s elapsed)<br></div><div> MUT time 0.324s ( 0.322s elapsed)<br></div><div> GC time 1.355s ( 1.357s elapsed)<br></div><div> EXIT time 0.000s ( 0.000s elapsed)<br></div><div> Total time 1.679s ( 1.679s elapsed)<br></div><div><br></div><div> %GC time 0.0% (0.0% elapsed)<br></div><div><br></div><div> Alloc rate 3,956,490,811 bytes per MUT second<br></div><div><br></div><div> Productivity 19.3% of total user, 19.2% of total elapsed<br></div><div></div><div>```<br><br>I compiled both with `ghc -O2 -rtsopts` and run them with `./Mean +RTS -s` (one does not nead to link the profiled RTS for `-s` to work).<br><br>Cheers,<br>Marcin<br><br></div><div class="protonmail_signature_block"><div class="protonmail_signature_block-user protonmail_signature_block-empty"></div><div class="protonmail_signature_block-proton">Sent with <a href="https://protonmail.com/" target="_blank">ProtonMail</a> Secure Email.</div></div><div><br></div><div class="protonmail_quote">
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐<br>
On Monday, May 10th, 2021 at 14:23, David James <dj112358@outlook.com> wrote:<br>
<blockquote class="protonmail_quote" type="cite">
<div>
<p>Ugh – sent too soon!</p>
<p> </p>
<p>Hello – I wanted to add some comments. mean is as you describe.</p>
<p> </p>
<p>mean1 as defined can take a list of any Real type, and return any Fractional type. These types need not be the same, and it’s up to the *<b>caller</b>* of mean1 to say what types they want to give and get returned, e.g. the following is
possible:</p>
<p> </p>
<p><span style="font-family:"Courier New"">> :m +Data.Ratio</span></p>
<p><span style="font-family:"Courier New"">> :m +Data.Complex</span></p>
<p><span style="font-family:"Courier New"">> mean1 [6%5, 2%3] :: Complex Double</span></p>
<p><span style="font-family:"Courier New"">0.9333333333333333 :+ 0.0</span></p>
<p> </p>
<p>This may not be what you were expecting? There’s also no need to restrict to Real, since it is valid to calculate the mean of a list of e.g. complex numbers. So maybe you want something like this:</p>
<p> </p>
<p><span style="font-family:"Courier New"">mean2 :: (Fractional a) => [a] -> a</span></p>
<p><span style="font-family:"Courier New"">mean2 xs = sum xs / genericLength xs</span></p>
<p> </p>
<p>(the realToFrac is now unnecessary). The caller still decides the type, but the argument and result type now have to be the same.</p>
<p> </p>
<div>
<p>You can’t now do <span style="font-family:"Courier New"">mean2 foo</span>, but you can do
<span style="font-family:"Courier New"">mean2 [1,2,3]</span> (and the 1, 2, 3 are interpreted as the default fractional type, probably Double).</p>
<p> </p>
</div>
<p>I personally prefer to write “utility” functions to be as generic as possible. (Imagine you’re including them in some standard library for the whole world to use). But I’m sure there are other opinions.</p>
<p> </p>
<p>Re performance, there is a comment against “genericLength” to say that it is not as efficient as length. And, as used above, this is the case. So maybe you actually want:</p>
<p> </p>
<p><span style="font-family:"Courier New"">mean3 :: (Fractional a) => [a] -> a</span></p>
<p><span style="font-family:"Courier New"">mean3 xs = sum xs / fromIntegral (length xs)</span></p>
<p> </p>
<p>which is as efficient as mean.</p>
<p> </p>
<p>If you are especially interested in performance, you might want to read
<a href="http://book.realworldhaskell.org/read/profiling-and-optimization.html" target="_blank" rel="noreferrer nofollow noopener">this</a>. It’s not really “beginner level” but does look at some issues with mean in some detail (and in particular why it can use so much memory and what you can do about that).
Performance of Haskell code is often more difficult to predict than for other languages, for example due to lazy evaluation and some amazing transformations of you code done by GHC.</p>
<p> </p>
<p>Incidentally, I am in the middle of drafting a <a href="https://en.wikibooks.org/wiki/User:Davjam2/Numbers" target="_blank" rel="noreferrer nofollow noopener">
page about numbers</a> to include in the Haskell wikibook that might be interesting. It discusses types, classes, defaults numeric conversions, etc. I would be very happy if for any feedback (please leave any on the “Discussion” tab on the page).</p>
<p> </p>
<p>FYI: I tested performance (on GHC 8.4.3 on Windows) with this:</p>
<p> </p>
<p><span style="font-family:"Courier New"">{-</span></p>
<p><span style="font-family:"Courier New"">compile: ghc -O2 -prof -fprof-auto -rtsopts Mean.hs</span></p>
<p><span style="font-family:"Courier New"">run: Mean +RTS -p > nul</span></p>
<p><span style="font-family:"Courier New"">-}</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">module Main (main) where</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">import Data.List</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">mean :: [Double] -> Double</span></p>
<p><span style="font-family:"Courier New"">mean xs = sum xs / fromIntegral (length xs)</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">mean1 :: (Real a, Fractional b) => [a] -> b</span></p>
<p><span style="font-family:"Courier New"">mean1 xs = realToFrac (sum xs) / genericLength xs</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">mean2 :: (Fractional a) => [a] -> a</span></p>
<p><span style="font-family:"Courier New"">mean2 xs = sum xs / genericLength xs</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">mean3 :: (Fractional a) => [a] -> a</span></p>
<p><span style="font-family:"Courier New"">mean3 xs = sum xs / fromIntegral (length xs)</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">mean4 :: (Fractional a) => [a] -> a</span></p>
<p><span style="font-family:"Courier New"">mean4 xs = sum xs / fromIntegral (genericLength xs :: Int)</span></p>
<p><span style="font-family:"Courier New""> </span></p>
<p><span style="font-family:"Courier New"">main :: IO ()</span></p>
<p><span style="font-family:"Courier New"">main = do</span></p>
<p><span style="font-family:"Courier New""> let xs = [1 .. 10000000] :: [Double] --may default to Integer unless specified as Double.</span></p>
<p><span style="font-family:"Courier New""> print $ mean xs --change to mean1, etc, for different runs</span></p>
<p> </p>
<p>And got:</p>
<p> </p>
<table style="border-collapse:collapse;border:none" cellspacing="0" cellpadding="0" border="1">
<tbody>
<tr>
<td style="width:47.55pt;border:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="63" valign="top">
<p> </p>
</td>
<td style="width:62.55pt;border:solid windowtext 1.0pt;border-left:none;padding:0cm 5.4pt 0cm 5.4pt" width="83" valign="top">
<p>Total time</p>
</td>
<td style="width:108.3pt;border:solid windowtext 1.0pt;border-left:none;padding:0cm 5.4pt 0cm 5.4pt" width="144" valign="top">
<p>Total alloc</p>
</td>
</tr>
<tr>
<td style="width:47.55pt;border:solid windowtext 1.0pt;border-top:none;padding:0cm 5.4pt 0cm 5.4pt" width="63" valign="top">
<p>mean</p>
</td>
<td style="width:62.55pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="83" valign="top">
<p>0.10</p>
</td>
<td style="width:108.3pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="144" valign="top">
<p>1,680,096,640 bytes</p>
</td>
</tr>
<tr>
<td style="width:47.55pt;border:solid windowtext 1.0pt;border-top:none;padding:0cm 5.4pt 0cm 5.4pt" width="63" valign="top">
<p>mean1</p>
</td>
<td style="width:62.55pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="83" valign="top">
<p>0.17</p>
</td>
<td style="width:108.3pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="144" valign="top">
<p>2,560,096,656 bytes</p>
</td>
</tr>
<tr>
<td style="width:47.55pt;border:solid windowtext 1.0pt;border-top:none;padding:0cm 5.4pt 0cm 5.4pt" width="63" valign="top">
<p>mean2</p>
</td>
<td style="width:62.55pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="83" valign="top">
<p>0.17</p>
</td>
<td style="width:108.3pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="144" valign="top">
<p>2,560,096,656 bytes</p>
</td>
</tr>
<tr>
<td style="width:47.55pt;border:solid windowtext 1.0pt;border-top:none;padding:0cm 5.4pt 0cm 5.4pt" width="63" valign="top">
<p>mean3</p>
</td>
<td style="width:62.55pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="83" valign="top">
<p>0.10</p>
</td>
<td style="width:108.3pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="144" valign="top">
<p>1,680,096,640 bytes</p>
</td>
</tr>
<tr>
<td style="width:47.55pt;border:solid windowtext 1.0pt;border-top:none;padding:0cm 5.4pt 0cm 5.4pt" width="63" valign="top">
<p>mean4</p>
</td>
<td style="width:62.55pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="83" valign="top">
<p>0.10</p>
</td>
<td style="width:108.3pt;border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 5.4pt 0cm 5.4pt" width="144" valign="top">
<p>1,680,096,640 bytes</p>
</td>
</tr>
</tbody>
</table>
<p> </p>
<p>mean4 also uses genericLength but is as fast as length. This is due to some cleaverness in GHC, where it uses more efficient code when it knows the required result is integral.</p>
<p> </p>
<p> </p>
<p><img proton-src="cid:image001.png@01D7459A.0824F980" style="width:7.2291in;height:.0208in" referrerpolicy="no-referrer" class="proton-embedded" alt="C41BD4BD0CBD4F519B49D2A93C03CE4A.png" src="cid:image001.png@01D7459A.0824F980" width="694" height="2" border="0"></p>
<div>
<p><b><span style="color:black">From:</span></b><span style="color:black"> Beginners <beginners-bounces@haskell.org> on behalf of Joe King <joeking1809@yahoo.com><br>
<b>Sent:</b> Saturday, May 8, 2021 10:39:50 AM<br>
<b>To:</b> beginners@haskell.org <beginners@haskell.org><br>
<b>Subject:</b> [Haskell-beginners] Function to compute the mean</span> </p>
<div>
<p> </p>
</div>
</div>
<p>Greeetings I am new here and pretty new to Haskell.<br>
<br>
I was wondering what are the relative advanatges/disadvatnages of specifying a mean function in these two ways:<br>
<br>
mean :: [Double] -> Double<br>
mean xs = sum xs / fromIntegral (length xs)<br>
<br>
and<br>
<br>
mean1 :: (Real a, Fractional b) => [a] -> b<br>
mean1 xs = realToFrac (sum xs) / genericLength xs<br>
<br>
I understand that mean1 has the advantage that it can be called with lists of any Real type, so would work with things like <br>
<br>
foo :: [Int]<br>
foo = [1,2,3]<br>
<br>
mean foo<br>
-- type mismatch error<br>
<br>
mean1 foo<br>
-- no error<br>
<br>
But suppose that I know I will only ever use lists of Double, is there still any advantage (or disadvantage of using mean1). For example is there any performance benefit by using mean in that case since mean1 has additional function evaluation. <br>
<br>
Are there any other considerations ?<br>
<br>
Thanks in advance<br>
JK<br>
_______________________________________________<br>
Beginners mailing list<br>
Beginners@haskell.org<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners" target="_blank" rel="noreferrer nofollow noopener">http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners</a></p>
<p> </p>
</div>
</blockquote><br>
</div>