ahammel wrote:A few things jump out at me comparing them:
General readability: Python is a bit better here. I think we can all agree that `if next_candidate in lazy_sieve' is a bit clearer than `if lazy_sieve.count(next_candidate)' (and can I point out that `count' is a pretty stupid name for a method that can only count to one?). The `yield' keyword is very handy for this particular algorithm in Python, but C++ doesn't have that, so I've had to do some slightly evil recursion magic that even the compiler complains about. Also `std::unordered_map<unsigned long, unsigned long> lazy_sieve'. I wrote it, and I can barely remember what the hell all that means.
std::unordered_map<unsigned long, unsigned long> looks very clear to me: It's a map that maps longs to longs.
Boilerplate: Python wins. Leaving aside the issue of the separation of variable instantiation in C++, there's a bit of language-specific weirdness in the implementation already that doesn't have much to do with the algorithm, particularly the header guard. I know the header guard isn't necessary, but it's a good idea if the program is ever going to be expanded beyond just printing primes.
Brevity: 42 non-trivial SLOC in the C++ vs 15 in the Python for similar or slightly better readability. Python wins.
The entire header file is not necessary. The Python implementation contains of a single function, it would be fairer to compare that to a C++ implementation that only consists of a single function. You could also strip a few lines consisting of variables declarations and closing braces only without decreasing readability. But of course your argument remains valid and C++ usually requires much more boilerplate than Python.
Int management: Python does it automatically, but it's a bit annoying in C++. The Python interpretation also has a slight advantage in that it will run until your exhaust either you memory or your patience, whereas I've had the C++ implementation error out once it gets to 18446744073709551615 rather than suffer an integer overflow. Sorry about that.
Other way to look at that: The Python implementation uses unnecessary large and complicated data types to store ints that cannot be properly optimized.
Memory management: perhaps surprisingly it's a tie. I haven't had to do any more explicit memory management in C++ than in the Python implementation, and it's valgrind-pure. I can't say as I understand exactly how everything gets cleaned up in my version, but then I don't understand how the Python garbage collector works either.
std::unordered_map has a non-trivial destructor that deletes the memory required to hold the hash map. When your class is destructed it implicitly calls this destructor.