http://www.johndcook.com/blog/2016/06/08/computing-higher-moments-with-a-fold/
Here is the C++ version:
#include <iostream>
#include <array>
#include <numeric>
#include <cmath>
int main()
{
auto sample = {2,30,51,72};
auto m = std::accumulate(std::begin(sample), std::end(sample), std::array<double, 5>{0, 0, 0, 0, 0},
[](auto& m, const auto& x) -> decltype(auto)
{
++m[0];
auto delta = x - m[1];
auto delta_n = delta / m[0];
auto delta_n2 = delta_n * delta_n;
auto t = delta * delta_n * (m[0]-1);
m[1] += delta_n;
m[4] += t * delta_n2 * (m[0]*m[0] - 3*m[0] + 3) + 6 * delta_n2 * m[2] - 4 * delta_n * m[3];
m[3] += t * delta_n * (m[0] - 2) - 3 * delta_n * m[2];
m[2] += t;
return m;
});
auto mvsk = {m[1], m[2]/(m[0]-1), std::pow(m[0], 0.5)*m[3]/std::pow(m[2], 1.5), m[0]*m[4]/m[2]/m[2] - 3};
for(auto m : mvsk) std::cout << m << ' ';
std::cout << std::endl;
return 0;
}
Unlike the Haskell version, this is not functional, as it uses side-effects: namely, it updates the array in place (ie, “auto& m”). To make it functional, simply remove the ampersand for value-passing semantics.
It’s very interesting that it is possible to write C++ in such an expressive yet compact manner. As the author noted, the OO C++ version is around twice the size, whereas this matches Haskell almost line for line. Technically, the std::accumulate could be written as a simple range-based for loop (also possible in C++), but having a named algorithm on the same order as Haskell’s foldl is much more self-documenting. Also having a self-contained lambda function makes design much more composable in a way that a for loop isn’t. The OO version is probably less readable at first glance too.
