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.