pure is a keyword in D2 that means something different than how you're using 
it. I also find it unusual that you use D2 range terminology but then give 
examples that don't use D ranges!

I'm also fairly ignorant about what is in std.algorithm, but you should be able 
to find things that look more like your functional code examples.

bearophile Wrote:

> This is a post of unfocused musings, so you can ignore this post if you have 
> better things to do.
> 
> 'generators' are something like structs that contain an opApply or the 
> methods of the Range protocol:
> 
> 
> import std.stdio: write;
> 
> struct XPrimes {
>     int stop;
> 
>     int opApply(int delegate(ref int) dg) {
>         int result;
>         foreach (x; 2 .. stop) {
>             bool all = true;
>             foreach (i; 2 .. x)
>                 if (!(x % i)) {
>                     all = false;
>                     break;
>                 }
>             if (all) {
>                 result = dg(x);
>                 if (result)
>                     break;
>             }
>         }
>         return result;
>     }
> }
> 
> void main() {
>     foreach (p; XPrimes(30))
>         write(p, " ");
> }
> 
> 
> 
> This is how you can do the same thing in Python, the code is a bit shorter:
> 
> 
> >>> p = (x for x in xrange(2,30) if all(x % i for i in xrange(2,x)))
> >>> list(p)
> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
> >>> list(p)
> []
> 
> 
> 
> That code also shows that Python generators can be used only once. So using 
> Range speech, they are all "Input Ranges", I presume to avoid indemonstrable 
> assumptions about their nature that can lead to bugs.
> 
> In D the situation is different, this prints the primes two times:
> 
> 
> import std.stdio: write, writeln;
> 
> class XPrimes {
>     int stop;
>     this(int s) { stop = s; }
> 
>     int opApply(int delegate(ref int) dg) {
>         int result;
>         foreach (x; 2 .. stop) {
>             bool all = true;
>             foreach (i; 2 .. x)
>                 if (!(x % i)) {
>                     all = false;
>                     break;
>                 }
>             if (all) {
>                 result = dg(x);
>                 if (result)
>                     break;
>             }
>         }
>         return result;
>     }
> }
> 
> void main() {
>     auto primes = new XPrimes(30);
>     foreach (p; primes)
>         write(p, " ");
>     writeln();
>     foreach (p; primes)
>         write(p, " ");
> }
> 
> 
> 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.
> 
> 
> 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.
> 
> 
> In theory the D Ranges can grow have a standard constant that the programmer 
> can add to tell if it's an Input Range or Forward Range. I don't know if this 
> is a good idea, the compiler can't verify that this constant value is right.
> 
> So in practice the idea of pure generators can be seen as a way (that the 
> compiler is able to verify) that a generator is not just an Input Range, but 
> a Forward Range. It can be possible to test it using similar ways used to 
> test that a function marked with pure is actually pure.
> 
> What can pure generators be useful for in D?
> 
> In Haskell many "generators" are pure, this allows to do things like:
> 
> 
> fibonacci = 1 : 1 : zipWith (+) fibonacci (tail fibonacci)
> fibs = take 20 fibonacci
> main = print fibs
> 
> 
> That prints:
> [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]
> 
> 
> The meaning of that little Haskell program is simple:
> fibonacci is a symbol, it can be seen as a lazy generator that yields a list 
> of numbers. The first and second are 1 (the colon stands for list item 
> concatenation), to it concatenates the zipping between the result of the 
> generator and another "copy" of the same generator minus the first item.
> 
> 
> You can write the same algorithm in Python using lazy iterators:
> 
> 
> from itertools import izip, islice
> 
> def fibonacci():
>     yield 1
>     yield 1
>     for nm1, n in izip(fibonacci(), islice(fibonacci(), 1, None)):
>         yield nm1 + n
> 
> fibs = islice(fibonacci(), 0, 20)
> print list(fibs)
> 
> 
> (I have tried to write this same program in D1 but it seems rather 
> impossible. I think it's possible in D2 but its debugging gets too much 
> complex for my tastes. In D I'd like a nicer yield as in Python and Chapel).
> 
> Even if doesn't show, the Haskell compiler is able to optimize quite well 
> that little program, it can generate many Fibonacci numbers in a short time, 
> while that Python code is really slow, uses tons of RAM, and it soon blows up 
> the stack. (Lazyness in Python is easy to use, but it was bolted on Python at 
> the last minute and it's not always good enough).
> 
> A pure generator means that it always yields the same sequence of outputs 
> given the same input. This allows for optimizations done by Haskell compiler 
> in that example. In theory a D compiler can do the same optimizations.
> 
> 
> 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;
>     }
> }
> 
> I am quite ignorant still about such topics of lazy pure functional 
> programming.
> 
> Bye,
> bearophile

Reply via email to