At first I wondered why he calls the monadic flatmap operation "for_each" but it makes sense in that he's actually creating a (lazy) list comprehension DSL here. The code within main() is mostly equivalent to the following Python code:
triples = ((a, b, c) for c in itertools.count()
for a in range(1, c+1)
for b in range(a, c+1)
if a*a + b*b == c*c)
for (a,b,c) in itertools.islice(triples, 10):
print("{}, {}, {}".format(a,b,c))
The equivalent in Rust, just because I'm currently learning it: let triples = (1..)
.flat_map(move |c| (1..=c)
.flat_map(move |a| (a..=c)
.flat_map(move |b| Some((a, b, c))
.filter(|_| a*a + b*b == c*c))));
for (a,b,c) in triples.take(10) {
println!("{}, {}, {}", a, b, c)
}
slavik81 showed a simpler for_each in a comment;
auto for_each = [](R&& r, Fun fun) {
return r | view::transform(fun) | view::join;
};
similarly, it seems that yield_if can also be radically simplified auto yield_if = [](bool b, T x) {
return b ? view::single(x)
: view::empty;
};
But if you actually read the blog post... for_each and yield_if are a part of the range_v3 library and are proposed additions to the language.Also I don't know what the heck is up with that cout statement; we can use structured declarations to make that look more pythonic too. And there's also a proposal for string formatting [1]
#include "fmt/format.h"
#include
#include
using namespace std;
using namespace view;
using namespace fmt;
int main() {
auto triples =
for_each(iota(1), [](int z) {
return for_each(iota(1, z+1), [=](int x) {
return for_each(iota(x, z+1), [=](int y) {
return yield_if(x*x + y*y == z*z,
make_tuple(x, y, z));
});
});
});
for(auto [x,y,z] : triples | take(10))
cout << format("{}, {}, {}", a, b, c) << endl;
}
So despite all the protests about how ugly Eric's solution is... I don't think the above is significantly worse than your python & rust equivalents. Speaking as a python evangelist who uses C++ out of nece$$ity.