mirror of
https://github.com/dlang/phobos.git
synced 2025-05-05 09:30:49 +03:00
Merge pull request #720 from jmdavis/8334
Fix for issue# 8334: find cannot handle close match at end of haystack in needle isn't bi-directional
This commit is contained in:
commit
47204076d8
2 changed files with 236 additions and 27 deletions
|
@ -3201,8 +3201,8 @@ unittest
|
|||
// Leftover specialization: searching a random-access range for a
|
||||
// non-bidirectional forward range
|
||||
R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
|
||||
if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2
|
||||
&& is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
|
||||
if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2 &&
|
||||
is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
|
||||
{
|
||||
static if (!is(ElementType!R1 == ElementType!R2))
|
||||
{
|
||||
|
@ -3211,11 +3211,25 @@ if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2
|
|||
else
|
||||
{
|
||||
// Prepare the search with needle's first element
|
||||
if (needle.empty) return haystack;
|
||||
if (needle.empty)
|
||||
return haystack;
|
||||
|
||||
haystack = .find!pred(haystack, needle.front);
|
||||
if (haystack.empty) return haystack;
|
||||
|
||||
static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == typeof(haystack)))
|
||||
{
|
||||
if (needle.length > haystack.length)
|
||||
return takeNone(haystack);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (haystack.empty)
|
||||
return haystack;
|
||||
}
|
||||
|
||||
needle.popFront();
|
||||
size_t matchLen = 1;
|
||||
|
||||
// Loop invariant: haystack[0 .. matchLen] matches everything in
|
||||
// the initial needle that was popped out of needle.
|
||||
for (;;)
|
||||
|
@ -3223,11 +3237,22 @@ if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2
|
|||
// Extend matchLength as much as possible
|
||||
for (;;)
|
||||
{
|
||||
if (needle.empty || haystack.empty) return haystack;
|
||||
if (!binaryFun!pred(haystack[matchLen], needle.front)) break;
|
||||
if (needle.empty || haystack.empty)
|
||||
return haystack;
|
||||
|
||||
static if(hasLength!R1 && is(typeof(takeNone(haystack)) == typeof(haystack)))
|
||||
{
|
||||
if(matchLen == haystack.length)
|
||||
return takeNone(haystack);
|
||||
}
|
||||
|
||||
if (!binaryFun!pred(haystack[matchLen], needle.front))
|
||||
break;
|
||||
|
||||
++matchLen;
|
||||
needle.popFront();
|
||||
}
|
||||
|
||||
auto bestMatch = haystack[0 .. matchLen];
|
||||
haystack.popFront();
|
||||
haystack = .find!pred(haystack, bestMatch);
|
||||
|
@ -3241,6 +3266,19 @@ unittest
|
|||
assert(find([ 1, 2, 1, 2, 3, 3 ], SList!int(2, 3)[]) == [ 2, 3, 3 ]);
|
||||
}
|
||||
|
||||
//Bug# 8334
|
||||
unittest
|
||||
{
|
||||
auto haystack = [1, 2, 3, 4, 1, 9, 12, 42];
|
||||
auto needle = [12, 42, 27];
|
||||
|
||||
//different overload of find, but it's the base case.
|
||||
assert(find(haystack, needle).empty);
|
||||
|
||||
assert(find(haystack, takeExactly(filter!"true"(needle), 3)).empty);
|
||||
assert(find(haystack, filter!"true"(needle)).empty);
|
||||
}
|
||||
|
||||
// Internally used by some find() overloads above. Can't make it
|
||||
// private due to bugs in the compiler.
|
||||
/*private*/ R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, R2 needle)
|
||||
|
|
213
std/range.d
213
std/range.d
|
@ -2871,9 +2871,6 @@ Returns a range with at most one element; for example, $(D
|
|||
takeOne([42, 43, 44])) returns a range consisting of the integer $(D
|
||||
42). Calling $(D popFront()) off that range renders it empty.
|
||||
|
||||
Sometimes an empty range with the same signature is needed. For such
|
||||
ranges use $(D takeNone!R()). For example:
|
||||
|
||||
----
|
||||
auto s = takeOne([42, 43, 44]);
|
||||
static assert(isRandomAccessRange!(typeof(s)));
|
||||
|
@ -2887,19 +2884,15 @@ assert(s[0] == 43);
|
|||
s.popFront();
|
||||
assert(s.length == 0);
|
||||
assert(s.empty);
|
||||
s = takeNone!(int[])();
|
||||
assert(s.length == 0);
|
||||
assert(s.empty);
|
||||
----
|
||||
|
||||
In effect $(D takeOne(r)) is somewhat equivalent to $(D take(r, 1)) and
|
||||
$(D takeNone(r)) is equivalent to $(D take(r, 0)), but in certain
|
||||
interfaces it is important to know statically that the range may only
|
||||
In effect $(D takeOne(r)) is somewhat equivalent to $(D take(r, 1)) but in
|
||||
certain interfaces it is important to know statically that the range may only
|
||||
have at most one element.
|
||||
|
||||
The type returned by $(D takeOne) and $(D takeNone) is a random-access
|
||||
range with length regardless of $(D R)'s capability (another feature
|
||||
that distinguishes $(D takeOne)/$(D takeNone) from $(D take)).
|
||||
The type returned by $(D takeOne) is a random-access range with length
|
||||
regardless of $(D R)'s capabilities (another feature that distinguishes
|
||||
$(D takeOne) from $(D take)).
|
||||
*/
|
||||
auto takeOne(R)(R source) if (isInputRange!R)
|
||||
{
|
||||
|
@ -2935,12 +2928,6 @@ auto takeOne(R)(R source) if (isInputRange!R)
|
|||
}
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
auto takeNone(R)() if (isInputRange!R)
|
||||
{
|
||||
return typeof(takeOne(R.init)).init;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto s = takeOne([42, 43, 44]);
|
||||
|
@ -2955,9 +2942,193 @@ unittest
|
|||
s.popFront();
|
||||
assert(s.length == 0);
|
||||
assert(s.empty);
|
||||
s = takeNone!(int[])();
|
||||
assert(s.length == 0);
|
||||
assert(s.empty);
|
||||
}
|
||||
|
||||
/++
|
||||
Returns an empty range which is statically known to be empty and is
|
||||
guaranteed to have $(D length) and be random access regardless of $(D R)'s
|
||||
capabilities.
|
||||
|
||||
Examples:
|
||||
--------------------
|
||||
auto range = takeNone!(int[])();
|
||||
assert(range.length == 0);
|
||||
assert(range.empty);
|
||||
--------------------
|
||||
+/
|
||||
auto takeNone(R)()
|
||||
if(isInputRange!R)
|
||||
{
|
||||
return typeof(takeOne(R.init)).init;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto range = takeNone!(int[])();
|
||||
assert(range.length == 0);
|
||||
assert(range.empty);
|
||||
|
||||
enum ctfe = takeNone!(int[])();
|
||||
static assert(ctfe.length == 0);
|
||||
static assert(ctfe.empty);
|
||||
}
|
||||
|
||||
|
||||
/++
|
||||
Creates an empty range from the given range in $(BIGOH 1). If it can, it
|
||||
will return the same range type. If not, it will return
|
||||
$(D takeExactly(range, 0)).
|
||||
|
||||
Examples:
|
||||
--------------------
|
||||
assert(takeNone([42, 27, 19]).empty);
|
||||
assert(takeNone("dlang.org").empty);
|
||||
assert(takeNone(filter!"true"([42, 27, 19])).empty);
|
||||
--------------------
|
||||
+/
|
||||
auto takeNone(R)(R range)
|
||||
if(isInputRange!R)
|
||||
{
|
||||
//Makes it so that calls to takeNone which don't use UFCS still work with a
|
||||
//member version if it's defined.
|
||||
static if(is(typeof(R.takeNone)))
|
||||
auto retval = range.takeNone();
|
||||
//@@@BUG@@@ 8339
|
||||
else static if(isDynamicArray!R)/+ ||
|
||||
(is(R == struct) && __traits(compiles, {auto r = R.init;}) && R.init.empty))+/
|
||||
{
|
||||
auto retval = R.init;
|
||||
}
|
||||
//An infinite range sliced at [0 .. 0] would likely still not be empty...
|
||||
else static if(hasSlicing!R && !isInfinite!R)
|
||||
auto retval = range[0 .. 0];
|
||||
else
|
||||
auto retval = takeExactly(range, 0);
|
||||
|
||||
//@@@BUG@@@ 7892 prevents this from being done in an out block.
|
||||
assert(retval.empty);
|
||||
return retval;
|
||||
}
|
||||
|
||||
//Verify Examples.
|
||||
unittest
|
||||
{
|
||||
assert(takeNone([42, 27, 19]).empty);
|
||||
assert(takeNone("dlang.org").empty);
|
||||
assert(takeNone(filter!"true"([42, 27, 19])).empty);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
import std.metastrings;
|
||||
|
||||
string genInput()
|
||||
{
|
||||
return "@property bool empty() { return _arr.empty; }" ~
|
||||
"@property auto front() { return _arr.front; }" ~
|
||||
"void popFront() { _arr.popFront(); }" ~
|
||||
"static assert(isInputRange!(typeof(this)));";
|
||||
}
|
||||
|
||||
static struct NormalStruct
|
||||
{
|
||||
//Disabled to make sure that the takeExactly version is used.
|
||||
@disable this();
|
||||
this(int[] arr) { _arr = arr; }
|
||||
mixin(genInput());
|
||||
int[] _arr;
|
||||
}
|
||||
|
||||
static struct SliceStruct
|
||||
{
|
||||
@disable this();
|
||||
this(int[] arr) { _arr = arr; }
|
||||
mixin(genInput());
|
||||
auto opSlice(size_t i, size_t j) { return typeof(this)(_arr[i .. j]); }
|
||||
int[] _arr;
|
||||
}
|
||||
|
||||
static struct InitStruct
|
||||
{
|
||||
mixin(genInput());
|
||||
int[] _arr;
|
||||
}
|
||||
|
||||
static struct TakeNoneStruct
|
||||
{
|
||||
this(int[] arr) { _arr = arr; }
|
||||
@disable this();
|
||||
mixin(genInput());
|
||||
auto takeNone() { return typeof(this)(null); }
|
||||
int[] _arr;
|
||||
}
|
||||
|
||||
static class NormalClass
|
||||
{
|
||||
this(int[] arr) {_arr = arr;}
|
||||
mixin(genInput());
|
||||
int[] _arr;
|
||||
}
|
||||
|
||||
static class SliceClass
|
||||
{
|
||||
this(int[] arr) { _arr = arr; }
|
||||
mixin(genInput());
|
||||
auto opSlice(size_t i, size_t j) { return new typeof(this)(_arr[i .. j]); }
|
||||
int[] _arr;
|
||||
}
|
||||
|
||||
static class TakeNoneClass
|
||||
{
|
||||
this(int[] arr) { _arr = arr; }
|
||||
mixin(genInput());
|
||||
auto takeNone() { return new typeof(this)(null); }
|
||||
int[] _arr;
|
||||
}
|
||||
|
||||
foreach(range; TypeTuple!(`[1, 2, 3, 4, 5]`,
|
||||
`"hello world"`,
|
||||
`"hello world"w`,
|
||||
`"hello world"d`,
|
||||
`SliceStruct([1, 2, 3])`,
|
||||
//@@@BUG@@@ 8339 forces this to be takeExactly
|
||||
//`InitStruct([1, 2, 3])`,
|
||||
`TakeNoneStruct([1, 2, 3])`))
|
||||
{
|
||||
mixin(Format!("enum a = takeNone(%s).empty;", range));
|
||||
assert(a, typeof(range).stringof);
|
||||
mixin(Format!("assert(takeNone(%s).empty);", range));
|
||||
mixin(Format!("static assert(is(typeof(%s) == typeof(takeNone(%s))), typeof(%s).stringof);",
|
||||
range, range, range));
|
||||
}
|
||||
|
||||
foreach(range; TypeTuple!(`NormalStruct([1, 2, 3])`,
|
||||
`InitStruct([1, 2, 3])`))
|
||||
{
|
||||
mixin(Format!("enum a = takeNone(%s).empty;", range));
|
||||
assert(a, typeof(range).stringof);
|
||||
mixin(Format!("assert(takeNone(%s).empty);", range));
|
||||
mixin(Format!("static assert(is(typeof(takeExactly(%s, 0)) == typeof(takeNone(%s))), typeof(%s).stringof);",
|
||||
range, range, range));
|
||||
}
|
||||
|
||||
//Don't work in CTFE.
|
||||
auto normal = new NormalClass([1, 2, 3]);
|
||||
assert(takeNone(normal).empty);
|
||||
static assert(is(typeof(takeExactly(normal, 0)) == typeof(takeNone(normal))), typeof(normal).stringof);
|
||||
|
||||
auto slice = new SliceClass([1, 2, 3]);
|
||||
assert(takeNone(slice).empty);
|
||||
static assert(is(SliceClass == typeof(takeNone(slice))), typeof(slice).stringof);
|
||||
|
||||
auto taken = new TakeNoneClass([1, 2, 3]);
|
||||
assert(takeNone(taken).empty);
|
||||
static assert(is(TakeNoneClass == typeof(takeNone(taken))), typeof(taken).stringof);
|
||||
|
||||
auto filtered = filter!"true"([1, 2, 3, 4, 5]);
|
||||
assert(takeNone(filtered).empty);
|
||||
//@@@BUG@@@ 8339 and 5941 force this to be takeExactly
|
||||
//static assert(is(typeof(filtered) == typeof(takeNone(filtered))), typeof(filtered).stringof);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue