Splitting Ranges using Lambda Predicates
monarch_dodra via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Wed Jun 11 01:58:56 PDT 2014
On Tuesday, 10 June 2014 at 22:31:37 UTC, Nordlöw wrote:
>> Either way, it shouldn't be too hard to implement. Base it off
>> "splitter!pred", which is actually quite trivial. AFAIK, your
>
> What do you mean by basing it off splitter!pred - should I
> start with some existing splitter algorithm in Phobos or start
> from scratch?
>
> Thx.
I meant mostly copy pasting it, and modifying it to your needs.
For example, I adapted it into this. For simplicity, I stripped
infinite and forward only range support. The only functions I
actually modified were "findTerminator", to actually find
according to what I want, and popFront.
//----
auto slicer(alias isTerminator, Range)(Range input)
if (((isRandomAccessRange!Range && hasSlicing!Range) ||
isSomeString!Range)
&& is(typeof(unaryFun!isTerminator(input.front))))
{
return SlicerResult!(unaryFun!isTerminator, Range)(input);
}
private struct SlicerResult(alias isTerminator, Range)
{
alias notTerminator = not!isTerminator;
private Range _input;
private size_t _end = 0;
private void findTerminator()
{
auto r =
_input.save.find!(not!isTerminator).find!isTerminator();
_end = _input.length - r.length;
}
this(Range input)
{
_input = input;
if (!_input.empty)
findTerminator();
else
_end = size_t.max;
}
static if (isInfinite!Range)
enum bool empty = false; // Propagate infiniteness.
else
@property bool empty()
{
return _end == size_t.max;
}
@property auto front()
{
return _input[0 .. _end];
}
void popFront()
{
_input = _input[_end .. _input.length];
if (_input.empty)
{
_end = size_t.max;
return;
}
findTerminator();
}
@property typeof(this) save()
{
auto ret = this;
ret._input = _input.save;
return ret;
}
}
//----
This will split on before the first element where pred is true,
provided there are previous elements where pred is false:
//----
void main()
{
"SomeGreatVariableName" .slicer!isUpper.writeln();
"someGGGreatVariableName".slicer!isUpper.writeln();
"".slicer!isUpper.writeln();
"a".slicer!isUpper.writeln();
"A".slicer!isUpper.writeln();
}
//----
["Some", "Great", "Variable", "Name"]
["some", "GGGreat", "Variable", "Name"]
[]
["a"]
["A"]
//----
This may or may not be what you wanted, depending on how you want
to split "GGGreat". If you wanted it to simply split *ON* the
left of every capital letter, then you can modify the the find
terminator into:
private void findTerminator()
{
auto r = _input.save.dropOne.find!isTerminator;
_end = _input.length - r.length;
}
And you'd get:
["some", "G", "G", "Great", "Variable", "Name"]
*******************************************
*******************************************
*******************************************
In any case, yeah, it shouldn't be too hard to shape it into what
you want. A more involved solution to this problem could be to
simply pass a "searchFun" predicate, in which case you'd be able
to split not just according to any "unitary predicate", but
according to an entire "range search strategy:
//----
auto slicer(alias searchFun, Range)(Range input)
if (((isRandomAccessRange!Range && hasSlicing!Range) ||
isSomeString!Range)
&& is(typeof(searchFun(input))))
{
return SlicerResult!(searchFun, Range)(input);
}
private struct SlicerResult(alias searchFun, Range)
{
private Range _input;
private size_t _end = 0;
private void findTerminator()
{
auto r = searchFun(_input.save);
_end = _input.length - r.length;
}
...
//----
And then:
"SomeGGGGreatVariableName".slicer!((s)=>s.find!isLower.find!isUpper).writeln();
"someGGGreatVariableName"
.slicer!((s)=>s.dropOne.find!isUpper).writeln();
["Some", "GGGGreat", "Variable", "Name"]
["some", "G", "G", "Great", "Variable", "Name"]
Just ideas.
More information about the Digitalmars-d-learn
mailing list