mirror of
https://github.com/dlang/phobos.git
synced 2025-05-03 00:20:26 +03:00
Made Splitter bidirectional. Also, Splitter now return an empty item at the end of the iteration if the input ends with a separator.
This commit is contained in:
parent
0d3ba39d3a
commit
2a1def14ff
1 changed files with 99 additions and 20 deletions
127
std/algorithm.d
127
std/algorithm.d
|
@ -608,32 +608,53 @@ assert(i == 3);
|
||||||
*/
|
*/
|
||||||
struct Splitter(Range, Separator)
|
struct Splitter(Range, Separator)
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
Range _input;
|
Range _input;
|
||||||
Separator _separator;
|
Separator _separator;
|
||||||
size_t _chunkLength;
|
size_t _frontLength = size_t.max;
|
||||||
|
static if (isBidirectionalRange!Range)
|
||||||
|
size_t _backLength = size_t.max;
|
||||||
|
enum bool separatorIsRange =
|
||||||
|
!is(typeof(ElementType!Range.init == _separator));
|
||||||
|
|
||||||
private Range search()
|
static if (separatorIsRange)
|
||||||
{
|
{
|
||||||
return find(_input, _separator);
|
size_t separatorLength() { return _separator.length; }
|
||||||
}
|
|
||||||
|
|
||||||
private void advance()
|
|
||||||
{
|
|
||||||
static if (is(typeof(_separator.length)))
|
|
||||||
{
|
|
||||||
_chunkLength += _separator.length;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
++_chunkLength;
|
enum size_t separatorLength = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensureFrontLength()
|
||||||
|
{
|
||||||
|
if (_frontLength != _frontLength.max) return;
|
||||||
|
// compute front length
|
||||||
|
_frontLength = _input.length - find(_input, _separator).length;
|
||||||
|
if (_frontLength == _input.length) _backLength = _frontLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensureBackLength()
|
||||||
|
{
|
||||||
|
if (_backLength != _backLength.max) return;
|
||||||
|
// compute back length
|
||||||
|
static if (separatorIsRange)
|
||||||
|
{
|
||||||
|
_backLength = _input.length -
|
||||||
|
find(retro(_input), retro(_separator)).length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_backLength = _input.length -
|
||||||
|
find(retro(_input), _separator).length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
this(Range input, Separator separator)
|
this(Range input, Separator separator)
|
||||||
{
|
{
|
||||||
_input = input;
|
_input = input;
|
||||||
_separator = separator;
|
_separator = separator;
|
||||||
_chunkLength = _input.length - search().length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ref auto opSlice()
|
ref auto opSlice()
|
||||||
|
@ -643,7 +664,8 @@ struct Splitter(Range, Separator)
|
||||||
|
|
||||||
Range front()
|
Range front()
|
||||||
{
|
{
|
||||||
return _input[0 .. _chunkLength];
|
ensureFrontLength;
|
||||||
|
return _input[0 .. _frontLength];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty()
|
bool empty()
|
||||||
|
@ -653,19 +675,64 @@ struct Splitter(Range, Separator)
|
||||||
|
|
||||||
void popFront()
|
void popFront()
|
||||||
{
|
{
|
||||||
if (_chunkLength == _input.length)
|
assert(!empty);
|
||||||
|
ensureFrontLength;
|
||||||
|
if (_frontLength == _input.length)
|
||||||
{
|
{
|
||||||
_input = _input[_chunkLength .. _input.length];
|
// done
|
||||||
|
_input = _input[_frontLength .. _frontLength];
|
||||||
|
_frontLength = 0;
|
||||||
|
_backLength = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
advance;
|
if (_frontLength && _frontLength + separatorLength == _input.length)
|
||||||
_input = _input[_chunkLength .. _input.length];
|
{
|
||||||
_chunkLength = _input.length - search().length;
|
// Special case: popping the first-to-last item; there is
|
||||||
|
// an empty item right after this. Leave the separator in.
|
||||||
|
_input = _input[_frontLength .. _input.length];
|
||||||
|
_frontLength = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Normal case, pop one item and the separator, get ready for
|
||||||
|
// reading the next item
|
||||||
|
_input = _input[_frontLength + separatorLength .. _input.length];
|
||||||
|
_frontLength = _frontLength.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range back()
|
||||||
|
{
|
||||||
|
ensureBackLength;
|
||||||
|
return _input[_input.length - _backLength .. _input.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
void popBack()
|
||||||
|
{
|
||||||
|
ensureBackLength;
|
||||||
|
if (_backLength == _input.length)
|
||||||
|
{
|
||||||
|
// done
|
||||||
|
_input = _input[0 .. 0];
|
||||||
|
_frontLength = 0;
|
||||||
|
_backLength = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_backLength && _backLength + separatorLength == _input.length)
|
||||||
|
{
|
||||||
|
// Special case: popping the first-to-last item; there is
|
||||||
|
// an empty item right before this. Leave the separator in.
|
||||||
|
_input = _input[0 .. _input.length - _backLength];
|
||||||
|
_backLength = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Normal case
|
||||||
|
_input = _input[0 .. _input.length - _backLength - separatorLength];
|
||||||
|
_backLength = _backLength.max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/// Ditto
|
||||||
Splitter!(Range, Separator) splitter(Range, Separator)(Range r, Separator s)
|
Splitter!(Range, Separator)
|
||||||
|
splitter(Range, Separator)(Range r, Separator s)
|
||||||
if (is(typeof(ElementType!(Range).init == ElementType!(Separator).init)) ||
|
if (is(typeof(ElementType!(Range).init == ElementType!(Separator).init)) ||
|
||||||
is(typeof(ElementType!(Range).init == Separator.init)))
|
is(typeof(ElementType!(Range).init == Separator.init)))
|
||||||
{
|
{
|
||||||
|
@ -677,18 +744,30 @@ unittest
|
||||||
auto s = ",abc, de, fg,hi,";
|
auto s = ",abc, de, fg,hi,";
|
||||||
auto sp0 = splitter(s, ',');
|
auto sp0 = splitter(s, ',');
|
||||||
//foreach (e; sp) writeln("[", e, "]");
|
//foreach (e; sp) writeln("[", e, "]");
|
||||||
assert(equal(sp0, ["", "abc", " de", " fg", "hi"][]));
|
assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][]));
|
||||||
|
|
||||||
auto s1 = ", abc, de, fg, hi, ";
|
auto s1 = ", abc, de, fg, hi, ";
|
||||||
auto sp1 = splitter(s1, ", ");
|
auto sp1 = splitter(s1, ", ");
|
||||||
//foreach (e; sp1) writeln("[", e, "]");
|
//foreach (e; sp1) writeln("[", e, "]");
|
||||||
assert(equal(sp1, ["", "abc", "de", " fg", "hi"][]));
|
assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][]));
|
||||||
|
|
||||||
int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ];
|
int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ];
|
||||||
int[][] w = [ [1, 2], [3], [4, 5] ];
|
int[][] w = [ [1, 2], [3], [4, 5], [] ];
|
||||||
uint i;
|
uint i;
|
||||||
foreach (e; splitter(a, 0)) assert(e == w[i++]);
|
foreach (e; splitter(a, 0))
|
||||||
assert(i == 3);
|
{
|
||||||
|
assert(i < w.length);
|
||||||
|
assert(e == w[i++]);
|
||||||
|
}
|
||||||
|
assert(i == w.length);
|
||||||
|
// Now go back
|
||||||
|
//auto s = splitter(a, 0);
|
||||||
|
foreach_reverse (e; splitter(a, 0))
|
||||||
|
{
|
||||||
|
assert(i > 0);
|
||||||
|
assert(equal(e, w[--i]), text(e));
|
||||||
|
}
|
||||||
|
assert(i == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// uniq
|
// uniq
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue