Issue 14909: Provided a fix for the forward range version of chunkBy so that it can accept predicates refernce variables in the calling function

This commit is contained in:
James Gray 2020-09-10 20:42:17 +02:00
parent 56b1919e9b
commit 2ed6952a71
2 changed files with 117 additions and 75 deletions

View file

@ -2001,38 +2001,26 @@ if (isInputRange!Range && !isForwardRange!Range)
} }
} }
} }
// Outer range for forward range version of chunkBy
// Single-pass implementation of chunkBy for forward ranges. private struct ChunkByOuter(Range)
private struct ChunkByImpl(alias pred, Range)
if (isForwardRange!Range)
{
import std.typecons : RefCounted;
enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
static if (isUnary)
alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
else
alias eq = binaryFun!pred;
// Outer range
static struct Impl
{ {
size_t groupNum; size_t groupNum;
Range current; Range current;
Range next; Range next;
} }
// Inner range // Inner range for forward range version of chunkBy
static struct Group private struct ChunkByGroup(alias eq, Range)
{ {
import std.typecons : RefCounted;
private size_t groupNum; private size_t groupNum;
private Range start; private Range start;
private Range current; private Range current;
private RefCounted!Impl mothership; private RefCounted!(ChunkByOuter!Range) mothership;
this(RefCounted!Impl origin) this(RefCounted!(ChunkByOuter!Range) origin)
{ {
groupNum = origin.groupNum; groupNum = origin.groupNum;
@ -2075,13 +2063,20 @@ if (isForwardRange!Range)
return copy; return copy;
} }
} }
static assert(isForwardRange!Group);
private RefCounted!Impl impl; // Single-pass implementation of chunkBy for forward ranges.
private struct ChunkByImpl(alias pred, alias eq, bool isUnary, Range)
if (isForwardRange!Range)
{
import std.typecons : RefCounted;
static assert(isForwardRange!(ChunkByGroup!(eq,Range)));
private RefCounted!(ChunkByOuter!Range) impl;
this(Range r) this(Range r)
{ {
impl = RefCounted!Impl(0, r, r.save); impl = RefCounted!(ChunkByOuter!Range)(0, r, r.save);
} }
@property bool empty() { return impl.current.empty; } @property bool empty() { return impl.current.empty; }
@ -2091,11 +2086,11 @@ if (isForwardRange!Range)
static if (isUnary) static if (isUnary)
{ {
import std.typecons : tuple; import std.typecons : tuple;
return tuple(unaryFun!pred(impl.current.front), Group(impl)); return tuple(unaryFun!pred(impl.current.front), ChunkByGroup!(eq,Range)(impl));
} }
else else
{ {
return Group(impl); return ChunkByGroup!(eq,Range)(impl);
} }
} }
@ -2126,6 +2121,43 @@ if (isForwardRange!Range)
~ " must be a forward range"); ~ " must be a forward range");
} }
//Test for https://issues.dlang.org/show_bug.cgi?id=14909
@system unittest
{
import std.algorithm.comparison : equal;
import std.typecons : tuple;
import std.stdio;
auto n = 3;
auto s = [1,2,3].chunkBy!(a => a+n);
auto t = s.save.map!(x=>x[0]);
auto u = s.map!(x=>x[1]);
assert(t.equal([4,5,6]));
assert(u.equal!equal([[1],[2],[3]]));
}
//Test for https://issues.dlang.org/show_bug.cgi?id=18751
@system unittest
{
import std.algorithm.comparison : equal;
string[] data = [ "abc", "abc", "def" ];
int[] indices = [ 0, 1, 2 ];
auto chunks = indices.chunkBy!((i, j) => data[i] == data[j]);
assert(chunks.equal!equal([ [ 0, 1 ], [ 2 ] ]));
}
//Additional test for fix for issues 14909 and 18751
@system unittest
{
import std.algorithm.comparison : equal;
auto v = [2,4,8,3,6,9,1,5,7];
auto i = 2;
assert(v.chunkBy!((a,b)=>a%i==b%i).equal!equal([[2,4,8],[3],[6],[9,1,5,7]]));
}
@system unittest @system unittest
{ {
import std.algorithm.comparison : equal; import std.algorithm.comparison : equal;
@ -2223,6 +2255,16 @@ if (isForwardRange!Range)
auto chunkBy(alias pred, Range)(Range r) auto chunkBy(alias pred, Range)(Range r)
if (isInputRange!Range) if (isInputRange!Range)
{ {
enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
static if (isUnary)
alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
else
alias eq = binaryFun!pred;
static if(isForwardRange!Range)
return ChunkByImpl!(pred, eq, isUnary, Range)(r);
else
return ChunkByImpl!(pred, Range)(r); return ChunkByImpl!(pred, Range)(r);
} }

View file

@ -1158,7 +1158,7 @@ template compose(fun...)
alias fun1 = unaryFun!(fun[1]); alias fun1 = unaryFun!(fun[1]);
// protein: the core composition operation // protein: the core composition operation
typeof({ E a; return fun0(fun1(a)); }()) compose(E)(E a) auto compose(E)(E a)
{ {
return fun0(fun1(a)); return fun0(fun1(a));
} }