Thread overview
emit: generalizes map, filter, joiner [proposal + implementation]
Mar 21, 2016
Timothee Cour
Mar 21, 2016
Seb
Mar 21, 2016
Tamas
Mar 23, 2016
Nick Treleaven
Mar 23, 2016
Tamas
Mar 24, 2016
Nick Treleaven
Mar 24, 2016
thedeemon
Mar 23, 2016
crimaniak
Mar 25, 2016
cy
March 21, 2016
given fun(put, a) a lambda that can call $put 0 or more times, some_range.emit!fun computes a range formed of all the calls to $put

eg:
assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a);}).equal([1, 9, 25, 49]));

in this case it can be done by combining map and filter but in other cases emit is more powerful and an equivalent (with map,filter,joiner) can be inefficient and more complex.

see https://github.com/timotheecour/dtools/blob/master/dtools/util/emit.d

could that be turned into std.algorithm.iteration.emit (after addressing
comments) ?

Any comments would be appreciated (especially regarding whether we can get rid of the 1st template argument with type deduction)


March 21, 2016
On Monday, 21 March 2016 at 11:35:49 UTC, Timothee Cour wrote:
> assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a);}).equal([1, 9, 25, 49]))

Could you try to point out whats wrong with map & filter?

assert(9.iota.filter!"a%2".map!"a*a".equal([1, 9, 25, 49])
March 21, 2016
On Monday, 21 March 2016 at 11:48:52 UTC, Seb wrote:
> Could you try to point out whats wrong with map & filter?
It's hard to do stuff like this:

assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a); if(a%3==0) put(a);}).equal([1,9,3,25,6,49]));

March 23, 2016
On Monday, 21 March 2016 at 23:09:27 UTC, Tamas wrote:
> On Monday, 21 March 2016 at 11:48:52 UTC, Seb wrote:
>> Could you try to point out whats wrong with map & filter?
> It's hard to do stuff like this:
>
> assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a); if(a%3==0) put(a);}).equal([1,9,3,25,6,49]));

Seems doable:
mapFilter!((a){if(a%2) return some(a*a); if(a%3==0) return some(a); return none;})
March 23, 2016
On Wednesday, 23 March 2016 at 17:29:55 UTC, Nick Treleaven wrote:
> On Monday, 21 March 2016 at 23:09:27 UTC, Tamas wrote:
>> On Monday, 21 March 2016 at 11:48:52 UTC, Seb wrote:
>>> Could you try to point out whats wrong with map & filter?
>> It's hard to do stuff like this:
>>
>> assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a); if(a%3==0) put(a);}).equal([1,9,3,25,6,49]));
>
> Seems doable:
> mapFilter!((a){if(a%2) return some(a*a); if(a%3==0) return some(a); return none;})

This one emits only 1 element when a==3. (BTW what is `mapFilter` and `some`?)
March 23, 2016
On Monday, 21 March 2016 at 11:48:52 UTC, Seb wrote:
> On Monday, 21 March 2016 at 11:35:49 UTC, Timothee Cour wrote:
>> assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a);}).equal([1, 9, 25, 49]))
I support idea to have such feature, sometimes it really need.

>
> Could you try to point out whats wrong with map & filter?
>
> assert(9.iota.filter!"a%2".map!"a*a".equal([1, 9, 25, 49])

map: 1 element -> 1 element
filter: 1 element -> 0..1 element(s)

emit: 1 element -> 0..infinite elements

This feature is more generic then map() and filter() together. But proposed implementation is not good. I think this is good place for generator function with yield.
If map() will accept generators and process it as expected, different from ordinary functions, then additional emit() method is not needed. And filter() too :)

March 24, 2016
On Monday, 21 March 2016 at 23:09:27 UTC, Tamas wrote:
> On Monday, 21 March 2016 at 11:48:52 UTC, Seb wrote:
>> Could you try to point out whats wrong with map & filter?
> It's hard to do stuff like this:
>
> assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a); if(a%3==0) put(a);}).equal([1,9,3,25,6,49]));

Is it a bug or you forgot 0 here?

How it looks with Phobos today:

    auto r = new Generator!int({
        9.iota.each!((a) { if (a % 2) yield(a*a); if (a % 3 == 0) yield(a); });
    });
    assert(r.equal([0,1,9,3,25,6,49]));

Probably much less efficient, of course.
March 24, 2016
On Wednesday, 23 March 2016 at 17:43:07 UTC, Tamas wrote:
> On Wednesday, 23 March 2016 at 17:29:55 UTC, Nick Treleaven wrote:
>> On Monday, 21 March 2016 at 23:09:27 UTC, Tamas wrote:
>>> On Monday, 21 March 2016 at 11:48:52 UTC, Seb wrote:
>>>> Could you try to point out whats wrong with map & filter?
>>> It's hard to do stuff like this:
>>>
>>> assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a); if(a%3==0) put(a);}).equal([1,9,3,25,6,49]));
>>
>> Seems doable:
>> mapFilter!((a){if(a%2) return some(a*a); if(a%3==0) return some(a); return none;})
>
> This one emits only 1 element when a==3.

Oops, thanks.

> (BTW what is `mapFilter` and `some`?)

It combines both functions, the lambda returns an Option type representing 0 or 1 element:
http://forum.dlang.org/post/ncom7m$1ebr$1@digitalmars.com


March 25, 2016
> template emit(T, alias fun)

I wonder if "template emit(alias fun, T)" wouldn't be a better idea. That way you could leave T open for type inference, even if you specify a function. D doesn't support reordering template arguments, so it's important to put the ones "most used" at the beginning.

Anyway, it looks neat. I hope you've looked at std.algorithm.iteration.reduce though? It does the same thing that emit does, and it's an ancient algorithm.

"fold" is just reduce with the arguments more convenient for D's syntax. But...


assert(9.iota.emit!(int,(put,a){if(a%2) put(a*a);}).equal([1, 9, 25, 49]));
=>
int[] intderp;
assert(9.iota.fold!((result,a) { if(a%2) return result ~ [a*a]; else return result;})(intderp) == ([1,9,25,49]));

What would be neat is a "reduce" that could produce an infinite range as a result. All I can do with reduce and fold is produce an eagerly evaluated growing array in memory, or something like chain(chain(chain(chain(chain(...))))) which uses up memory just from creating all those iterators. Like, I don't know how to use fold! to do this:

  auto natural_numbers = sequence!"n"();
  auto oddsquared = natural_numbers.filter!"a%2==1".map!"a*a"; // fold?
  assert(take(oddsquared,4).equal([1, 9, 25, 49]));