Every time I see "use ranges and algorithms!" examples, I am baffled that apparently, I am supposed to find
inline double algorithm_call(std::span<double const> xs) noexcept {
return std::accumulate(
xs.begin(),
xs.end(),
0.0,
[](double acc, double volts) {
auto mv = calibrated_mv(volts);
auto err = residual(mv);
return weighted_square(err) + acc;
});
}
more readable, concise, and easier on my eyes than inline double raw_loop(std::span<double const> xs) noexcept {
double sum = 0.0;
for (double volts : xs) {
auto mv = calibrated_mv(volts);
auto err = residual(mv);
sum += weighted_square(err);
}
return sum;
}
Sure, there are some algorithms in <algorithms> that I'm rather not reimplement myself, but this one is not it.The first form is easier to send to 32 beefy cores or 1024 small CPUs or a Beowulf cluster or a GPU or people sitting in a room.
Great, now use some functions. From the library or your own, and see this complexity become manageable.
That's what abstraction is about.
You said "ranges and algorithms", but you didn't copy the third function which actually uses <range> library.
inline double ranges_pipeline(std::span<double const> xs) noexcept { auto costs = xs | std::views::transform(calibrated_mv) | std::views::transform(residual) | std::views::transform(weighted_square);
}It's still a bit verbose, because C++ doesn't allow universal function call syntax. It will be even more concise in other languages like D.