Thread overview
yield, C# etc
Aug 13, 2008
bearophile
Aug 13, 2008
davidl
Aug 13, 2008
Denis Koroskin
Aug 13, 2008
bearophile
Aug 13, 2008
Denis Koroskin
Aug 13, 2008
Don
Aug 14, 2008
downs
August 13, 2008
This explains how C# implements a handy yield not far from the Python one (Python one allows to create coroutines too):

http://blogs.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx

The syntax of yield is quite cleaner than the opApply (but maybe that C# implementation is less efficient).

Bye,
bearophile
August 13, 2008
在 Wed, 13 Aug 2008 08:21:29 +0800,bearophile <bearophileHUGS@lycos.com> 写道:

> This explains how C# implements a handy yield not far from the Python one (Python one allows to create coroutines too):
>
> http://blogs.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx
>
> The syntax of yield is quite cleaner than the opApply (but maybe that C# implementation is less efficient).
>
> Bye,
> bearophile

iirc, C#'s yield also allows to create coroutine.

-- 
使用 Opera 革命性的电子邮件客户程序: http://www.opera.com/mail/
August 13, 2008
On Wed, 13 Aug 2008 06:02:13 +0400, davidl <davidl@126.com> wrote:

> 在 Wed, 13 Aug 2008 08:21:29 +0800,bearophile <bearophileHUGS@lycos.com> 写道:
>
>> This explains how C# implements a handy yield not far from the Python one (Python one allows to create coroutines too):
>>
>> http://blogs.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx
>>
>> The syntax of yield is quite cleaner than the opApply (but maybe that C# implementation is less efficient).
>>
>> Bye,
>> bearophile
>
> iirc, C#'s yield also allows to create coroutine.
>


I don't see much difference between "yield return i;" and "int result = dg(i);".
First one is slightly cleaner while second one allows overloading.

Everything else is an implementation details.
August 13, 2008
Denis Koroskin:
> I don't see much difference between "yield return i;" and "int result =
> dg(i);".
> First one is slightly cleaner while second one allows overloading.

You are right, probably I prefer Python semantics & syntax here, not C# (or D) ones. I most of the cases you don't need overloading, but you always want a nicer syntax. (For people coming from C++ syntax is less important, I know).


> Everything else is an implementation details.

Syntax matters, when you have recursive generators every bit of help you receive from the language is precious to avoid distracting you from messing the algorithm.
The current syntax of D for the lazy iteration is very bad.

The following series generates the inverse Gray code: http://www.research.att.com/projects/OEIS?Anum=A006068


In Python:

def A006068():
    yield 0
    for x in A006068():
        if x & 1:
            yield 2*x+1
            yield 2*x
        else:
            if x:
                yield 2*x
            yield 2*x+1


In D using my libs, that use code modified from Witold Baryluk:

struct A006068 {
    void generator() {
        yield(0);
        foreach(x; A006068()) {
            if (x & 1) {
                yield(2 * x + 1);
                yield(2 * x);
            } else {
                if (x)
                    yield(2 * x);
                yield(2 * x + 1);
            }
        }
    }
    mixin Generator!(int);
}


In normal D:

struct A006068b {
    int opApply(int delegate(ref int) dg) {
        int result, aux;
        aux = 0; result = dg(aux); if (result) return result;
        foreach(x; A006068b()) {
            if (x & 1) {
                aux = 2 * x + 1; result = dg(aux); if (result) break;
                aux = 2 * x; result = dg(aux); if (result) break;
            } else {
                if (x)
                    { aux = 2 * x; result = dg(aux); if (result) break; }
                aux = 2 * x + 1; result = dg(aux); if (result) break;
            }
        }
        return result;
    }
}

The D version is bad to read and bad to write, there's too much noise.

Bye,
bearophile
August 13, 2008
On Wed, 13 Aug 2008 15:49:58 +0400, bearophile <bearophileHUGS@lycos.com> wrote:

> In normal D:
>
> struct A006068b {
>     int opApply(int delegate(ref int) dg) {
>         int result, aux;
>         aux = 0; result = dg(aux); if (result) return result;
>         foreach(x; A006068b()) {
>             if (x & 1) {
>                 aux = 2 * x + 1; result = dg(aux); if (result) break;
>                 aux = 2 * x; result = dg(aux); if (result) break;
>             } else {
>                 if (x)
>                     { aux = 2 * x; result = dg(aux); if (result) break; }
>                 aux = 2 * x + 1; result = dg(aux); if (result) break;
>             }
>         }
>         return result;
>     }
> }
>
> The D version is bad to read and bad to write, there's too much noise.
>
> Bye,
> bearophile


template yield(char[] value)
{
    const char[] yield = "{ int aux = " ~ value ~ "; int res = dg(aux); if (res) { return res; } }";
}

struct A006068b {
    int opApply(int delegate(ref int) dg) {
        mixin(yield!("0"));
        foreach(int x; A006068b()) {
            if (x & 1) {
                mixin(yield!("2 * x + 1"));
                mixin(yield!("2 * x"));
            } else {
                if (x) {
                    mixin(yield!("2 * x"));
                }
                mixin(yield!("2 * x + 1"));
            }
        }

        return 0;
    }
}

I hope we will be able to write yield(2*x + 1); instead of mixin soon (as an AST macros or a built-in feature).
August 13, 2008
Denis Koroskin wrote:
> On Wed, 13 Aug 2008 15:49:58 +0400, bearophile <bearophileHUGS@lycos.com> wrote:
> 
>> In normal D:
>>
>> struct A006068b {
>>     int opApply(int delegate(ref int) dg) {
>>         int result, aux;
>>         aux = 0; result = dg(aux); if (result) return result;
>>         foreach(x; A006068b()) {
>>             if (x & 1) {
>>                 aux = 2 * x + 1; result = dg(aux); if (result) break;
>>                 aux = 2 * x; result = dg(aux); if (result) break;
>>             } else {
>>                 if (x)
>>                     { aux = 2 * x; result = dg(aux); if (result) break; }
>>                 aux = 2 * x + 1; result = dg(aux); if (result) break;
>>             }
>>         }
>>         return result;
>>     }
>> }
>>
>> The D version is bad to read and bad to write, there's too much noise.
>>
>> Bye,
>> bearophile
> 
> 
> template yield(char[] value)
> {
>     const char[] yield = "{ int aux = " ~ value ~ "; int res = dg(aux); if (res) { return res; } }";
> }
> 
> struct A006068b {
>     int opApply(int delegate(ref int) dg) {
>         mixin(yield!("0"));
>         foreach(int x; A006068b()) {
>             if (x & 1) {
>                 mixin(yield!("2 * x + 1"));
>                 mixin(yield!("2 * x"));
>             } else {
>                 if (x) {
>                     mixin(yield!("2 * x"));
>                 }
>                 mixin(yield!("2 * x + 1"));
>             }
>         }
> 
>         return 0;
>     }
> }
> 
> I hope we will be able to write yield(2*x + 1); instead of mixin soon (as an AST macros or a built-in feature).

Yup. And it'd be alright even without any AST stuff. Just something like:

mixin auto char[] yield(char [] value) {...}

to indicate that yield(x+2) automatically converts into mixin(yield("x+2"));

Then all the complicated AST stuff could be put in a library.




August 14, 2008
bearophile wrote:
> Denis Koroskin:
>> I don't see much difference between "yield return i;" and "int result =
>> dg(i);".
>> First one is slightly cleaner while second one allows overloading.
> 
> You are right, probably I prefer Python semantics & syntax here, not C# (or D) ones. I most of the cases you don't need overloading, but you always want a nicer syntax. (For people coming from C++ syntax is less important, I know).
> 
> 
>> Everything else is an implementation details.
> 
> Syntax matters, when you have recursive generators every bit of help you receive from the language is precious to avoid distracting you from messing the algorithm.
> The current syntax of D for the lazy iteration is very bad.
> 
> The following series generates the inverse Gray code: http://www.research.att.com/projects/OEIS?Anum=A006068
> 
> 
> In Python:
> 
> def A006068():
>     yield 0
>     for x in A006068():
>         if x & 1:
>             yield 2*x+1
>             yield 2*x
>         else:
>             if x:
>                 yield 2*x
>             yield 2*x+1
> 
> 
> In D using my libs, that use code modified from Witold Baryluk:
> 
> struct A006068 {
>     void generator() {
>         yield(0);
>         foreach(x; A006068()) {
>             if (x & 1) {
>                 yield(2 * x + 1);
>                 yield(2 * x);
>             } else {
>                 if (x)
>                     yield(2 * x);
>                 yield(2 * x + 1);
>             }
>         }
>     }
>     mixin Generator!(int);
> }
> 

FWIW, in tools:

module test92;
import tools.stackthreads, tools.log;

Source!(int) A006068() {
  return new Source!(int)((void delegate(int) yield) {
    yield(0);
    foreach (x; A006068()) {
      if (x & 1) {
        yield(2 * x + 1);
        yield(2 * x);
      } else {
        if (x) yield(2 * x);
        yield(2 * x + 1);
      }
    }
  });
}

void main() { foreach (v; A006068()) logln(v); }

Also .. is it expected that this does an infinite loop?