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:
Andrei Alexandrescu 2009-04-29 21:28:55 +00:00
parent 0d3ba39d3a
commit 2a1def14ff

View file

@ -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