bearophile <[email protected]> wrote:
In few more years of D learning I hope to understand why D iteration is
designed that way, and if there are problems in it.
This has to do with D iteration being more designed towards mimicking
C arrays.
In D2, you'd use a range, and possibly have popFront return the new
(changed) range.
In practice a generator like:
(x for x in xrange(2,N) if all(x % i for i in xrange(2,x)))
is kind of pure, because despite being lazy, the output it generates is
only influences by an input parameter N. So it's a Forward Range in D
speech.
Except that it also changes the input. It seems to me the python example
uses a closure, which, as Norman Adams allegedly said, "are a poor man's
objects." This closure must then change its associated state upon
iteration. (Granted, coroutines may be closer to the truth here, but I
have not quite grokked those yet, and the end result is basically the
same.)
pure yield(int) primes(int stop) {
foreach (x; 2 .. stop) {
// an all() HOF can help here
bool all = true;
foreach (i; 2 .. x)
if (!(x % i)) {
all = false;
break;
}
if (all)
yield x;
}
}
Here's an implementation that seems to work:
module foo;
import core.thread;
import std.stdio;
class YieldingFiber( T ) : Fiber {
this( void function( ) fn ) {
super( fn );
}
this( void delegate( ) fn ) {
super( fn );
}
T call( ) {
if ( auto o = super.call( ) ) {
throw o;
}
return _value;
}
static void yield( T value ) {
if ( auto p = cast( typeof( this ) )getThis( ) ) {
p._value = value;
}
Fiber.yield( );
}
private:
T _value;
}
void primes( ) {
int x = 2;
while ( true ) {
bool all = true;
foreach ( i; 2 .. x ) {
if ( !( x % i ) ) {
all = false;
break;
}
}
if ( all ) {
YieldingFiber!( int ).yield( x );
}
++x;
}
}
void main( ) {
auto primes = new YieldingFiber!( int )( &primes );
foreach ( i; 0..20 ) {
writeln( primes.call( ) );
}
}
I am quite ignorant still about such topics of lazy pure functional
programming.
Welcome to the club!
--
Simen