mirror of
https://github.com/dlang/phobos.git
synced 2025-05-06 11:07:39 +03:00
Merge pull request #2859 from kuettler/splitter_pred
Add predicate to splitter
This commit is contained in:
commit
919c4efa24
1 changed files with 98 additions and 11 deletions
109
std/algorithm.d
109
std/algorithm.d
|
@ -2994,14 +2994,20 @@ element. If a range with one separator is given, the result is a range
|
||||||
with two empty elements.
|
with two empty elements.
|
||||||
|
|
||||||
If splitting a string on whitespace and token compression is desired,
|
If splitting a string on whitespace and token compression is desired,
|
||||||
consider using $(D splitter) without specifying a separator (see third overload
|
consider using $(D splitter) without specifying a separator (see fourth overload
|
||||||
below).
|
below).
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
|
pred = The predicate for comparing each element with the separator,
|
||||||
|
defaulting to $(D "a == b").
|
||||||
r = The $(XREF2 range, isInputRange, input range) to be split. Must support
|
r = The $(XREF2 range, isInputRange, input range) to be split. Must support
|
||||||
slicing and $(D .length).
|
slicing and $(D .length).
|
||||||
s = The element to be treated as the separator between range segments to be
|
s = The element to be treated as the separator between range segments to be
|
||||||
split. Must be of the same type as the element type of $(D r).
|
split.
|
||||||
|
|
||||||
|
Constraints:
|
||||||
|
The predicate $(D pred) needs to accept an element of $(D r) and the
|
||||||
|
separator $(D s).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An input range of the subranges of elements between separators. If $(D r)
|
An input range of the subranges of elements between separators. If $(D r)
|
||||||
|
@ -3012,8 +3018,8 @@ See_Also:
|
||||||
$(XREF regex, _splitter) for a version that splits using a regular
|
$(XREF regex, _splitter) for a version that splits using a regular
|
||||||
expression defined separator.
|
expression defined separator.
|
||||||
*/
|
*/
|
||||||
auto splitter(Range, Separator)(Range r, Separator s)
|
auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
|
||||||
if (is(typeof(ElementType!Range.init == Separator.init))
|
if (is(typeof(binaryFun!pred(r.front, s)) : bool)
|
||||||
&& ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
|
&& ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
|
||||||
{
|
{
|
||||||
import std.conv : unsigned;
|
import std.conv : unsigned;
|
||||||
|
@ -3043,7 +3049,7 @@ if (is(typeof(ElementType!Range.init == Separator.init))
|
||||||
static IndexType lastIndexOf(Range haystack, Separator needle)
|
static IndexType lastIndexOf(Range haystack, Separator needle)
|
||||||
{
|
{
|
||||||
import std.range : retro;
|
import std.range : retro;
|
||||||
auto r = haystack.retro().find(needle);
|
auto r = haystack.retro().find!pred(needle);
|
||||||
return r.retro().length - 1;
|
return r.retro().length - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3081,7 +3087,7 @@ if (is(typeof(ElementType!Range.init == Separator.init))
|
||||||
assert(!empty);
|
assert(!empty);
|
||||||
if (_frontLength == _unComputed)
|
if (_frontLength == _unComputed)
|
||||||
{
|
{
|
||||||
auto r = _input.find(_separator);
|
auto r = _input.find!pred(_separator);
|
||||||
_frontLength = _input.length - r.length;
|
_frontLength = _input.length - r.length;
|
||||||
}
|
}
|
||||||
return _input[0 .. _frontLength];
|
return _input[0 .. _frontLength];
|
||||||
|
@ -3178,6 +3184,8 @@ if (is(typeof(ElementType!Range.init == Separator.init))
|
||||||
assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ]));
|
assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ]));
|
||||||
a = [ 0, 1 ];
|
a = [ 0, 1 ];
|
||||||
assert(equal(splitter(a, 0), [ [], [1] ]));
|
assert(equal(splitter(a, 0), [ [], [1] ]));
|
||||||
|
w = [ [0], [1], [2] ];
|
||||||
|
assert(equal(splitter!"a.front == b"(w, 1), [ [[0]], [[2]] ]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@safe unittest
|
@safe unittest
|
||||||
|
@ -3266,10 +3274,16 @@ the split range. Use $(D filter!(a => !a.empty)) on the result to compress
|
||||||
empty elements.
|
empty elements.
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
|
pred = The predicate for comparing each element with the separator,
|
||||||
|
defaulting to $(D "a == b").
|
||||||
r = The $(XREF2 range, isInputRange, input range) to be split.
|
r = The $(XREF2 range, isInputRange, input range) to be split.
|
||||||
s = The $(XREF2 range, isForwardRange, forward range) to be treated as the
|
s = The $(XREF2 range, isForwardRange, forward range) to be treated as the
|
||||||
separator between segments of $(D r) to be split.
|
separator between segments of $(D r) to be split.
|
||||||
|
|
||||||
|
Constraints:
|
||||||
|
The predicate $(D pred) needs to accept an element of $(D r) and an
|
||||||
|
element of $(D s).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An input range of the subranges of elements between separators. If $(D r)
|
An input range of the subranges of elements between separators. If $(D r)
|
||||||
is a forward range or bidirectional range, the returned range will be
|
is a forward range or bidirectional range, the returned range will be
|
||||||
|
@ -3278,8 +3292,8 @@ Returns:
|
||||||
See_Also: $(XREF regex, _splitter) for a version that splits using a regular
|
See_Also: $(XREF regex, _splitter) for a version that splits using a regular
|
||||||
expression defined separator.
|
expression defined separator.
|
||||||
*/
|
*/
|
||||||
auto splitter(Range, Separator)(Range r, Separator s)
|
auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
|
||||||
if (is(typeof(Range.init.front == Separator.init.front) : bool)
|
if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
|
||||||
&& (hasSlicing!Range || isNarrowString!Range)
|
&& (hasSlicing!Range || isNarrowString!Range)
|
||||||
&& isForwardRange!Separator
|
&& isForwardRange!Separator
|
||||||
&& (hasLength!Separator || isNarrowString!Separator))
|
&& (hasLength!Separator || isNarrowString!Separator))
|
||||||
|
@ -3305,7 +3319,7 @@ if (is(typeof(Range.init.front == Separator.init.front) : bool)
|
||||||
assert(!_input.empty);
|
assert(!_input.empty);
|
||||||
// compute front length
|
// compute front length
|
||||||
_frontLength = (_separator.empty) ? 1 :
|
_frontLength = (_separator.empty) ? 1 :
|
||||||
_input.length - find(_input, _separator).length;
|
_input.length - find!pred(_input, _separator).length;
|
||||||
static if (isBidirectionalRange!Range)
|
static if (isBidirectionalRange!Range)
|
||||||
if (_frontLength == _input.length) _backLength = _frontLength;
|
if (_frontLength == _input.length) _backLength = _frontLength;
|
||||||
}
|
}
|
||||||
|
@ -3320,7 +3334,7 @@ if (is(typeof(Range.init.front == Separator.init.front) : bool)
|
||||||
{
|
{
|
||||||
import std.range : retro;
|
import std.range : retro;
|
||||||
_backLength = _input.length -
|
_backLength = _input.length -
|
||||||
find(retro(_input), retro(_separator)).source.length;
|
find!pred(retro(_input), retro(_separator)).source.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3433,6 +3447,28 @@ if (is(typeof(Range.init.front == Separator.init.front) : bool)
|
||||||
return Result(r, s);
|
return Result(r, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
assert(equal(splitter("hello world", " "), [ "hello", "world" ]));
|
||||||
|
int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
|
||||||
|
int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
|
||||||
|
assert(equal(splitter(a, [0, 0]), w));
|
||||||
|
a = [ 0, 0 ];
|
||||||
|
assert(equal(splitter(a, [0, 0]), [ (int[]).init, (int[]).init ]));
|
||||||
|
a = [ 0, 0, 1 ];
|
||||||
|
assert(equal(splitter(a, [0, 0]), [ [], [1] ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
// There seems to be a difficulty in startsWith, so this needs further
|
||||||
|
// attention.
|
||||||
|
//auto m = [ ["k":0], ["k":1], ["k":1], ["k":2] ];
|
||||||
|
//bool pred(int[string] a, int b) { return a["k"] == b; }
|
||||||
|
//assert(equal(splitter!pred(m, [1, 1]), [ [[0]], [[2]] ]));
|
||||||
|
}
|
||||||
|
|
||||||
@safe unittest
|
@safe unittest
|
||||||
{
|
{
|
||||||
import std.conv : text;
|
import std.conv : text;
|
||||||
|
@ -3475,6 +3511,19 @@ if (is(typeof(Range.init.front == Separator.init.front) : bool)
|
||||||
assert(walkLength(words) == 5, text(walkLength(words)));
|
assert(walkLength(words) == 5, text(walkLength(words)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ];
|
||||||
|
int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ];
|
||||||
|
uint i;
|
||||||
|
foreach (e; splitter!"a.front == 0"(a, 0))
|
||||||
|
{
|
||||||
|
assert(i < w.length);
|
||||||
|
assert(e == w[i++]);
|
||||||
|
}
|
||||||
|
assert(i == w.length);
|
||||||
|
}
|
||||||
|
|
||||||
@safe unittest
|
@safe unittest
|
||||||
{
|
{
|
||||||
debug(std_algorithm) scope(success)
|
debug(std_algorithm) scope(success)
|
||||||
|
@ -3514,13 +3563,51 @@ if (is(typeof(Range.init.front == Separator.init.front) : bool)
|
||||||
assert(words.equal([ "i", "am", "pointing" ]));
|
assert(words.equal([ "i", "am", "pointing" ]));
|
||||||
}
|
}
|
||||||
|
|
||||||
///ditto
|
/**
|
||||||
|
|
||||||
|
Similar to the previous overload of $(D splitter), except this one does not use a separator.
|
||||||
|
Instead, the predicate is an unary function on the input range's element type.
|
||||||
|
|
||||||
|
Two adjacent separators are considered to surround an empty element in
|
||||||
|
the split range. Use $(D filter!(a => !a.empty)) on the result to compress
|
||||||
|
empty elements.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
isTerminator = The predicate for deciding where to split the range.
|
||||||
|
r = The $(XREF2 range, isInputRange, input range) to be split.
|
||||||
|
|
||||||
|
Constraints:
|
||||||
|
The predicate $(D isTerminator) needs to accept an element of $(D r).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An input range of the subranges of elements between separators. If $(D r)
|
||||||
|
is a forward range or bidirectional range, the returned range will be
|
||||||
|
likewise.
|
||||||
|
|
||||||
|
See_Also: $(XREF regex, _splitter) for a version that splits using a regular
|
||||||
|
expression defined separator.
|
||||||
|
*/
|
||||||
auto splitter(alias isTerminator, Range)(Range input)
|
auto splitter(alias isTerminator, Range)(Range input)
|
||||||
if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front))))
|
if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front))))
|
||||||
{
|
{
|
||||||
return SplitterResult!(unaryFun!isTerminator, Range)(input);
|
return SplitterResult!(unaryFun!isTerminator, Range)(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
assert(equal(splitter!"a == ' '"("hello world"), [ "hello", "", "world" ]));
|
||||||
|
int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
|
||||||
|
int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
|
||||||
|
assert(equal(splitter!"a == 0"(a), w));
|
||||||
|
a = [ 0 ];
|
||||||
|
assert(equal(splitter!"a == 0"(a), [ (int[]).init, (int[]).init ]));
|
||||||
|
a = [ 0, 1 ];
|
||||||
|
assert(equal(splitter!"a == 0"(a), [ [], [1] ]));
|
||||||
|
w = [ [0], [1], [2] ];
|
||||||
|
assert(equal(splitter!"a.front == 1"(w), [ [[0]], [[2]] ]));
|
||||||
|
}
|
||||||
|
|
||||||
private struct SplitterResult(alias isTerminator, Range)
|
private struct SplitterResult(alias isTerminator, Range)
|
||||||
{
|
{
|
||||||
enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range;
|
enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue