std.algorithm.splitter on a string not always bidirectional

Steven Schveighoffer schveiguy at gmail.com
Fri Jan 22 14:14:50 UTC 2021


On 1/22/21 12:55 AM, Jon Degenhardt wrote:
> On Friday, 22 January 2021 at 05:51:38 UTC, Jon Degenhardt wrote:
>> On Thursday, 21 January 2021 at 22:43:37 UTC, Steven Schveighoffer wrote:
>>> auto sp1 = "a|b|c".splitter('|');
>>>
>>> writeln(sp1.back); // ok
>>>
>>> auto sp2 = "a.b|c".splitter!(v => !isAlphaNum(v));
>>>
>>> writeln(sp2.back); // error, not bidirectional
>>>
>>> Why? is it an oversight, or is there a good reason for it?
>>>
>>
>> I believe the reason is two-fold. First, splitter is lazy. Second, the 
>> range splitting is defined in the forward direction, not the reverse 
>> direction. A bidirectional range is only supported if it is guaranteed 
>> that the splits will occur at the same points in the range when run in 
>> either direction. That's why the single element delimiter is 
>> supported. Its clearly the case for the predicate function in your 
>> example. If that's known to be always true then perhaps it would make 
>> sense to enhance splitter to generate bidirectional results in this case.
>>
> 
> Note that the predicate might use a random number generator to pick the 
> split points. Even for same sequence of random numbers, the split points 
> would be different if run from the front than if run from the back.

I think this isn't a good explanation.

All forms of splitter accept a predicate (including the one which 
supports a bi-directional result). Many other phobos algorithms that 
accept a predicate provide bidirectional support. The splitter result is 
also a forward range (which makes no sense in the context of random splits).

Finally, I'd suggest that even if you split based on a subrange that is 
also bidirectional, it doesn't make sense that you couldn't split 
backwards based on that. Common sense says a range split on substrings 
is the same whether you split it forwards or backwards.

I can do this too (and in fact I will, because it works, even though 
it's horrifically ugly):

auto sp3 = "a.b|c".splitter!((c, unused) => !isAlphaNum(c))('?');

writeln(sp3.back); // ok

Looking at the code, it looks like the first form of spltter uses a 
different result struct than the other two (which have a common 
implementation). It just needs cleanup.

-Steve


More information about the Digitalmars-d-learn mailing list