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:
Andrei Alexandrescu 2012-08-19 21:00:27 -07:00
commit 47204076d8
2 changed files with 236 additions and 27 deletions

View file

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

View file

@ -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);
}