Thread overview
yield iteration
Jul 29, 2012
anonymous
Jul 29, 2012
bearophile
Jul 29, 2012
Tobias Pankrath
Jul 29, 2012
bearophile
Jul 31, 2012
Christophe Travert
Jul 31, 2012
Christophe Travert
Jul 29, 2012
Timon Gehr
Jul 29, 2012
Timon Gehr
July 29, 2012
A subthread of the "Impressed" thread revolved around D's lack of a yield mechanism for iteration.
Nick Sabalausky mentioned his earlier attempt to create a library based yield which unfortunately was too slow to compete with explicit opApply/ranges [1].

I took a shot at it. It's fast. The resulting code is reasonably pretty. But it doesn't provide a range interface, just opApply.

---
module yielder;

import core.thread;

/**
	Examples:
	---
	import yielder;
	import std.stdio;
	
	auto y = new class Yielder!int {
		final void yielding() {
			yield(0);
			yield(13);
			yield(42);
		}
	};
	
	foreach(x; y) {
		writeln(x);
	}
	---
*/
class Yielder(E) {
	abstract void yielding();
	
	this() {
		this.fiber = new Fiber(&yielding);
	}
	
	private {
		Fiber fiber;
		int result;
		int delegate(ref E) callback;
	}
	
	final int opApply(int delegate(ref E) cb) {
		callback = cb;
		result = 0;
		fiber.reset();
		fiber.call();
		return result;
	}
	
	final void yield(ref E value) {
		result = callback(value);
		if(result != 0) {
			Fiber.yield();
		}
	}
	final void yield(E value) {
		yield(value);
	}
}
---

[1] https://semitwist.com/articles/article/view/combine-coroutines-and-input-ranges-for-dead-simple-d-iteration

July 29, 2012
anonymous:

> A subthread of the "Impressed" thread revolved around D's lack of a yield mechanism for iteration.

The yield as in Python is so useful and handy, and I miss it often in D.

This is an example of Inverse Gray iterator (Sloane series A006068) Python2 code from:
http://code.activestate.com/recipes/221457/

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


Turning that in D code that uses opApply is not hard, but the code inflates 3X, and you can't use most std.algorithm on it.

I have discussed the situation a little here:
http://d.puremagic.com/issues/show_bug.cgi?id=5660

Bye,
bearophile
July 29, 2012
>
> This is an example of Inverse Gray iterator (Sloane series A006068) Python2 code from:
> http://code.activestate.com/recipes/221457/
>
> 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
>

Does this spawn linear many generators?
July 29, 2012
Ideally it would look like this:

struct Map(alias fun, R){
    R range;
    mixin YieldInputRange!q{
        for(; !range.empty; range.popFront())
            yield fun(range.front);
    }
    static if(is(typeof(range.save))) @property auto save(){
        return Map!(fun, R)(range.save);
    }
    ...
}

Or as a built-in:

struct Map(alias fun, R){
    R range;
    yield {
        for(; !range.empty; range.popFront())
            yield fun(x);
    }
    static if(is(typeof(range.save))) @property auto save(){
        return Map!(fun, R)(range.save);
    }
    ...
}
July 29, 2012
On 07/29/2012 04:48 PM, Timon Gehr wrote:
> Ideally it would look like this:
>
> struct Map(alias fun, R){
>      R range;
>      mixin YieldInputRange!q{
>          for(; !range.empty; range.popFront())
>              yield fun(range.front);
>      }
>      static if(is(typeof(range.save))) @property auto save(){
>          return Map!(fun, R)(range.save);
>      }
>      ...
> }
>
> Or as a built-in:
>
> struct Map(alias fun, R){
>      R range;
>      yield {
>          for(; !range.empty; range.popFront())
>              yield fun(x);
               yield fun(range.front); // oops
>      }
>      static if(is(typeof(range.save))) @property auto save(){
>          return Map!(fun, R)(range.save);
>      }
>      ...
> }

July 29, 2012
Tobias Pankrath:

> Does this spawn linear many generators?

I have written this little test code, Python2.6+:


count = 0

def A006068():
    global count
    count += 1
    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

from itertools import islice

def main():
    global count
    for p in xrange(15):
        n = 2 ** p
        count = 0
        print sum(1 for _ in islice(A006068(), 0, n)), count

main()

The output seems to show that it spawns only logarithmically:


1 1
2 2
4 3
8 4
16 5
32 6
64 7
128 8
256 9
512 10
1024 11
2048 12
4096 13
8192 14
16384 15

Bye,
bearophile
July 31, 2012
"bearophile" , dans le message (digitalmars.D:173647), a écrit :
> Turning that in D code that uses opApply is not hard, but the code inflates 3X, and you can't use most std.algorithm on it.

I believe most std.algorithm that work on input range could be made to work with opApply, or opApply-like delegates. It just wouldn't be particularly efficient unless agressive inlining is used.

For example, filter could work like this for opApply-like delegates:

template filter(pred)
{
  auto filter(T)(int delegate(int delegate(ref T)) apply)
  {
    return (int delegate(ref T) dg)
    {
      return apply( (ref T t) { return pred(t)? dg(t): 1; });
    }
  }
}

-- 
Christophe
July 31, 2012
Christophe Travert, dans le message (digitalmars.D:173787), a écrit :
> "bearophile" , dans le message (digitalmars.D:173647), a écrit :
>> Turning that in D code that uses opApply is not hard, but the code inflates 3X, and you can't use most std.algorithm on it.
> 
> I believe most std.algorithm that work on input range could be made to work with opApply, or opApply-like delegates. It just wouldn't be particularly efficient unless agressive inlining is used.
> 
> For example, filter could work like this for opApply-like delegates:
> 
> template filter(pred)
> {
>   auto filter(T)(int delegate(int delegate(ref T)) apply)
>   {
>     return (int delegate(ref T) dg)
>     {
>       return apply( (ref T t) { return pred(t)? dg(t): 1; });
                                                         ^should read 0
>     }
>   }
> }
> 
> -- 
> Christophe