Bug 15357 – std.algorithm.iteration.each should mirror the behavior of foreach.
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2015-11-18T18:20:00Z
Last change time
2016-12-23T01:07:41Z
Assigned to
nobody
Creator
monkeyworks12
Comments
Comment #0 by monkeyworks12 — 2015-11-18T18:20:08Z
As the result of lockstep only defines an n-array opApply method (where n is the number of arguments passed to lockstep) and does not conform to the range interface, it does not properly inter-operate with each, which only works with ranges. The offending code:
void main(){
import std.container;
import std.stdio;
import std.algorithm.iteration;
import std.range;
Array!int ai = [1,2,3,4];
Array!int ai1 = [1,2,3,4];
Array!int ai2 = [1,2,3,4];
auto arange2 = lockstep(ai[],ai1[],ai2[]);
//Error: template std.algorithm.iteration.each cannot deduce function from
//argument types !((a, b, c) => writeln(a, b, c))(Lockstep!(RangeT!
//(Array!int), RangeT!(Array!int), RangeT!(Array!int))),
//candidates are:
// /opt/compilers/dmd2/include/std/algorithm/iteration.d(820):
// std.algorithm.iteration.each(alias pred = "a")
//arange2.each!((a,b,c) => writeln(a, b, c));
}
Furthermore, it *will* correctly work by accident if the user passes exactly two ranges where the type of each range's front is convertible to size_t, because it will interpret the first range as a range of indices.
In my opinion lockstep's opApply should be deprecated outright and replaced with a range interface that returns a reference to a tuple of its arguments.
Comment #1 by monkeyworks12 — 2015-11-18T18:24:08Z
To amend what I just said, each *does* support opApply to a degree, but only unary and binary versions (the latter of which still only takes a single range, and an index). I will open a separate issue for that.
Comment #2 by monkeyworks12 — 2015-11-18T18:31:35Z
From looking at the code of `each`, it seems to be intended to only work on opApply structs that only pass one value per iteration; if you instantiate `each` with a two-argument function, `each` supplies the index of the current element as the first parameter, and the element itself as the second element.
The problem is that the latter case is implemented via a standard foreach loop, i.e. `foreach(i, elem; iter)`; if iter returns pairs of elements, then the `i` value is hijacked to become the first element instead.
So the question is whether `each` should support n-ary opApply iterators at all. If yes, then the opApply case for `each` should be expanded; if no, then some additional checks should be placed on `each` to prevent accepting `opApply` functions that pass in more than 1 element.
Comment #4 by ryan — 2015-11-19T03:09:09Z
I think each should mirror the behavior of foreach.
Give it a range that returns one element, and the first variable is interpreted as an index. Give it a range that returns two elements, and each variable is one of those elements.
unittest {
import std.range, std.algorithm, std.stdio;
auto a = [ 'a', 'b', 'c' ];
auto b = [ 'd', 'e', 'f' ];
// print each character with an index
foreach(i, c ; a) writeln(i, " : ", c);
a.each!((i, c) => writeln(i, " : ", c));
// print pairs of characters
foreach(c1, c2 ; a.lockstep(b)) writeln(c1, " : ", c2);
a.lockstep(b).each!((c1, c2) => writeln(c1, " : ", c2));
}
Comment #5 by ryan — 2015-11-19T13:24:41Z
(In reply to monkeyworks12 from comment #0)
>
> In my opinion lockstep's opApply should be deprecated outright and replaced
> with a range interface that returns a reference to a tuple of its arguments.
The problem with returning a tuple from front is losing the ability to modify elements by ref. IIRC this is exactly what zip does. If we can return a tuple through a range interface that allows modifying elements by ref, then I think we could entirely deprecate lockstep in favor of zip.
Comment #6 by greeenify — 2016-12-23T00:38:58Z
you could define :
ref front() {
return
}
Comment #7 by greeenify — 2016-12-23T00:41:10Z
> I think each should mirror the behavior of foreach.
I agree - renamed the issue accordingly.
> If we can return a tuple through a range interface that allows modifying elements by ref, then I think we could entirely deprecate lockstep in favor of zip.
As far as I understand: with DIP1000 in master we should be able to do so :)
Comment #8 by greeenify — 2016-12-23T01:07:41Z
closing this as std.algorithm.iteration.each supports n-aray opApply since 2.072 thanks to @Ryan: https://github.com/dlang/phobos/pull/3837
Please reopen or create a new issue if you still experience troubles.