mirror of
https://github.com/dlang/phobos.git
synced 2025-05-11 14:41:08 +03:00
Improved startsWith and endsWith.
1. startsWith and endsWith now have similar implementations. 2. They work with strings of different character sizes, whereas before they treated strings as arrays regardless of whether they had the same character size or not. 3. endsWith now actually works when mixing ranges and elements in the endsWith portion like the documentation claims it does.
This commit is contained in:
parent
06dd205d28
commit
fcb672f55f
2 changed files with 316 additions and 157 deletions
471
std/algorithm.d
471
std/algorithm.d
|
@ -3742,34 +3742,33 @@ assert(startsWith("abc", "x", "aaa", "a", "sab") == 3);
|
|||
----
|
||||
*/
|
||||
uint startsWith(alias pred = "a == b", Range, Ranges...)
|
||||
(Range doesThisStart, Ranges withOneOfThese)
|
||||
if (Ranges.length > 1 && isInputRange!Range
|
||||
&& is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0]))
|
||||
: bool)
|
||||
&& is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1 .. $]))
|
||||
: uint))
|
||||
(Range doesThisStart, Ranges withOneOfThese)
|
||||
if(isInputRange!Range && Ranges.length > 1 &&
|
||||
is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0])) : bool ) &&
|
||||
is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1 .. $])) : uint))
|
||||
{
|
||||
alias doesThisStart lhs;
|
||||
alias withOneOfThese rhs;
|
||||
alias doesThisStart haystack;
|
||||
alias withOneOfThese needles;
|
||||
|
||||
// Make one pass looking for empty ranges
|
||||
foreach (i, Unused; Ranges)
|
||||
foreach(i, Unused; Ranges)
|
||||
{
|
||||
// Empty range matches everything
|
||||
static if (!is(typeof(binaryFun!pred(lhs.front, rhs[i])) : bool))
|
||||
static if(!is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
|
||||
{
|
||||
if (rhs[i].empty) return i + 1;
|
||||
if(needles[i].empty)
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (; !lhs.empty; lhs.popFront())
|
||||
for(; !haystack.empty; haystack.popFront())
|
||||
{
|
||||
foreach (i, Unused; Ranges)
|
||||
foreach(i, Unused; Ranges)
|
||||
{
|
||||
static if (is(typeof(binaryFun!pred(lhs.front, rhs[i])) : bool))
|
||||
static if(is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
|
||||
{
|
||||
// Single-element
|
||||
if (binaryFun!pred(lhs.front, rhs[i]))
|
||||
if (binaryFun!pred(haystack.front, needles[i]))
|
||||
{
|
||||
// found, but continue to account for one-element
|
||||
// range matches (consider startsWith("ab", "a",
|
||||
|
@ -3779,75 +3778,90 @@ if (Ranges.length > 1 && isInputRange!Range
|
|||
}
|
||||
else
|
||||
{
|
||||
if (binaryFun!pred(lhs.front, rhs[i].front))
|
||||
{
|
||||
if(binaryFun!pred(haystack.front, needles[i].front))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// This code executed on failure to match
|
||||
// Out with this guy, check for the others
|
||||
uint result = startsWith!pred(lhs, rhs[0 .. i], rhs[i + 1 .. $]);
|
||||
if (result > i) ++result;
|
||||
uint result = startsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]);
|
||||
if(result > i)
|
||||
++result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// If execution reaches this point, then the front matches for all
|
||||
// rhs ranges. What we need to do now is to lop off the front of
|
||||
// needles ranges. What we need to do now is to lop off the front of
|
||||
// all ranges involved and recurse.
|
||||
foreach (i, Unused; Ranges)
|
||||
foreach(i, Unused; Ranges)
|
||||
{
|
||||
static if (is(typeof(binaryFun!pred(lhs.front, rhs[i])) : bool))
|
||||
static if(is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
|
||||
{
|
||||
// Test has passed in the previous loop
|
||||
return i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs[i].popFront();
|
||||
if (rhs[i].empty) return i + 1;
|
||||
needles[i].popFront();
|
||||
if(needles[i].empty)
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Ditto
|
||||
bool startsWith(alias pred = "a == b", R1, R2)
|
||||
(R1 doesThisStart, R2 withThis)
|
||||
if (isInputRange!R1 && isInputRange!R2
|
||||
&& is(typeof(binaryFun!pred(doesThisStart.front, withThis.front))
|
||||
: bool))
|
||||
(R1 doesThisStart, R2 withThis)
|
||||
if(isInputRange!R1 &&
|
||||
isInputRange!R2 &&
|
||||
is(typeof(binaryFun!pred(doesThisStart.front, withThis.front)) : bool))
|
||||
{
|
||||
alias doesThisStart haystack;
|
||||
alias withThis needle;
|
||||
|
||||
// Special case for two arrays
|
||||
static if (isArray!R1 && isArray!R2)
|
||||
static if(isArray!R1 && isArray!R2 &&
|
||||
((!isSomeString!R1 && !isSomeString!R2) ||
|
||||
(isSomeString!R1 && isSomeString!R2 &&
|
||||
is(Unqual!(typeof(haystack[0])) == Unqual!(typeof(needle[0]))))))
|
||||
{
|
||||
alias doesThisStart haystack;
|
||||
alias withThis needle;
|
||||
//writeln("Matching: ", haystack, " with ", needle);
|
||||
if (haystack.length < needle.length) return 0;
|
||||
foreach (j; 0 .. needle.length)
|
||||
if(haystack.length < needle.length)
|
||||
return false;
|
||||
|
||||
foreach(j; 0 .. needle.length)
|
||||
{
|
||||
if (!binaryFun!pred(needle[j], haystack[j]))
|
||||
if(!binaryFun!pred(needle[j], haystack[j]))
|
||||
// not found
|
||||
return false;
|
||||
}
|
||||
|
||||
// found!
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
static if (hasLength!R1 && hasLength!R2)
|
||||
static if(hasLength!R1 && hasLength!R2)
|
||||
{
|
||||
if (doesThisStart.length < withThis.length) return false;
|
||||
if(haystack.length < needle.length)
|
||||
return false;
|
||||
}
|
||||
if (withThis.empty) return true;
|
||||
for (; !doesThisStart.empty; doesThisStart.popFront())
|
||||
|
||||
if(needle.empty)
|
||||
return true;
|
||||
|
||||
for(; !haystack.empty; haystack.popFront())
|
||||
{
|
||||
if (!binaryFun!pred(doesThisStart.front, withThis.front))
|
||||
if(!binaryFun!pred(haystack.front, needle.front))
|
||||
break;
|
||||
withThis.popFront();
|
||||
if (withThis.empty) return true;
|
||||
|
||||
needle.popFront();
|
||||
|
||||
if(needle.empty)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -3855,42 +3869,81 @@ if (isInputRange!R1 && isInputRange!R2
|
|||
|
||||
/// Ditto
|
||||
bool startsWith(alias pred = "a == b", R, E)
|
||||
(R doesThisStart, E withThis)
|
||||
if (isInputRange!R && is(typeof(binaryFun!pred(doesThisStart.front, withThis))
|
||||
: bool))
|
||||
(R doesThisStart, E withThis)
|
||||
if(isInputRange!R &&
|
||||
is(typeof(binaryFun!pred(doesThisStart.front, withThis)) : bool))
|
||||
{
|
||||
return doesThisStart.empty
|
||||
? false
|
||||
: binaryFun!pred(doesThisStart.front, withThis);
|
||||
return doesThisStart.empty ? false
|
||||
: binaryFun!pred(doesThisStart.front, withThis);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
debug(std_algorithm) scope(success)
|
||||
writeln("unittest @", __FILE__, ":", __LINE__, " done.");
|
||||
bool x = startsWith("ab", "a");
|
||||
assert(startsWith("abc", ""));
|
||||
assert(startsWith("abc", "a"));
|
||||
assert(!startsWith("abc", "b"));
|
||||
assert(!startsWith("abc", "b", "bc", "abcd", "xyz"));
|
||||
assert(startsWith("abc", "a", "b") == 1);
|
||||
assert(startsWith("abc", "b", "a") == 2);
|
||||
assert(startsWith("abc", "a", 'a') == 1);
|
||||
assert(startsWith("abc", 'a', "a") == 1);
|
||||
assert(startsWith("abc", "x", "a", "b") == 2);
|
||||
assert(startsWith("abc", "x", "aa", "ab") == 3);
|
||||
assert(startsWith("abc", "x", "aaa", "sab") == 0);
|
||||
assert(startsWith("abc", 'a'));
|
||||
assert(!startsWith("abc", "sab"));
|
||||
assert(startsWith("abc", 'x', "aaa", 'a', "sab") == 3);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
debug(std_algorithm) scope(success)
|
||||
writeln("unittest @", __FILE__, ":", __LINE__, " done.");
|
||||
assert(!startsWith("abc", 'x', 'n', 'b'));
|
||||
assert(startsWith("abc", 'x', 'n', 'a') == 3);
|
||||
foreach(S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
|
||||
{
|
||||
assert(!startsWith(to!S("abc"), 'a'));
|
||||
assert(startsWith(to!S("abc"), 'a', 'c') == 1);
|
||||
assert(!startsWith(to!S("abc"), 'x', 'n', 'b'));
|
||||
assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3);
|
||||
assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2);
|
||||
|
||||
foreach(T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
|
||||
{
|
||||
assert(startsWith(to!S("abc"), to!T("")));
|
||||
assert(startsWith(to!S("ab"), to!T("a")));
|
||||
assert(startsWith(to!S("abc"), to!T("a")));
|
||||
assert(!startsWith(to!S("abc"), to!T("b")));
|
||||
assert(!startsWith(to!S("abc"), to!T("b"), "bc", "abcd", "xyz"));
|
||||
assert(startsWith(to!S("abc"), to!T("ab"), 'a') == 1);
|
||||
assert(startsWith(to!S("abc"), to!T("a"), "b") == 1);
|
||||
assert(startsWith(to!S("abc"), to!T("b"), "a") == 2);
|
||||
assert(startsWith(to!S("abc"), to!T("a"), 'a') == 1);
|
||||
assert(startsWith(to!S("abc"), 'a', to!T("a")) == 1);
|
||||
assert(startsWith(to!S("abc"), to!T("x"), "a", "b") == 2);
|
||||
assert(startsWith(to!S("abc"), to!T("x"), "aa", "ab") == 3);
|
||||
assert(startsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0);
|
||||
assert(startsWith(to!S("abc"), 'a'));
|
||||
assert(!startsWith(to!S("abc"), to!T("sab")));
|
||||
assert(startsWith(to!S("abc"), 'x', to!T("aaa"), 'a', "sab") == 3);
|
||||
assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")));
|
||||
assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("Hel"), to!T("\uFF28el")) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], cast(int[])null));
|
||||
assert(!startsWith([0, 1, 2, 3, 4, 5], 5));
|
||||
assert(!startsWith([0, 1, 2, 3, 4, 5], 1));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], 0));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], 5, 1, 2) == 3);
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0]));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1]));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1], 7) == 1);
|
||||
assert(!startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7]));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7], [0, 1, 2]) == 2);
|
||||
|
||||
assert(!startsWith([0, 1, 2, 3, 4, 5], 1));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], 0));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], 5, 1, 2) == 3);
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0]));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1]));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1], 7) == 1);
|
||||
assert(!startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7]));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7], [0, 1, 2]) == 2);
|
||||
|
||||
assert(!startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), 1));
|
||||
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), 0));
|
||||
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0]));
|
||||
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1]));
|
||||
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1], 7) == 1);
|
||||
assert(!startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1, 7]));
|
||||
assert(startsWith(filter!"true"([0, 1, 2, 3, 4, 5]), [0, 1, 7], [0, 1, 2]) == 2);
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1])));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1]), 7));
|
||||
assert(!startsWith([0, 1, 2, 3, 4, 5], filter!"true"([0, 1, 7])));
|
||||
assert(startsWith([0, 1, 2, 3, 4, 5], [0, 1, 7], filter!"true"([0, 1, 2])) == 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3984,111 +4037,217 @@ assert(endsWith("abc", "x", "aaa", "sab") == 0);
|
|||
assert(endsWith("abc", "x", "aaa", 'c', "sab") == 3);
|
||||
----
|
||||
*/
|
||||
uint
|
||||
endsWith(alias pred = "a == b", Range, Ranges...)
|
||||
(Range doesThisEnd, Ranges withOneOfThese)
|
||||
if (isInputRange!(Range) && Ranges.length > 0
|
||||
&& is(typeof(binaryFun!pred(doesThisEnd.back, withOneOfThese[0].back))))
|
||||
uint endsWith(alias pred = "a == b", Range, Ranges...)
|
||||
(Range doesThisEnd, Ranges withOneOfThese)
|
||||
if(isInputRange!Range && Ranges.length > 1 &&
|
||||
is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) &&
|
||||
is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1 .. $])) : uint))
|
||||
{
|
||||
alias doesThisEnd lhs;
|
||||
alias withOneOfThese rhs;
|
||||
// Special case for two arrays
|
||||
static if (Ranges.length == 1 && isArray!Range && isArray!(Ranges[0])
|
||||
&& is(typeof(binaryFun!(pred)(lhs[0], rhs[0][0]))))
|
||||
alias doesThisEnd haystack;
|
||||
alias withOneOfThese needles;
|
||||
|
||||
// Make one pass looking for empty ranges
|
||||
foreach(i, Unused; Ranges)
|
||||
{
|
||||
if (lhs.length < rhs[0].length) return 0;
|
||||
auto k = lhs.length - rhs[0].length;
|
||||
foreach (j; 0 .. rhs[0].length)
|
||||
// Empty range matches everything
|
||||
static if(!is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
|
||||
{
|
||||
if (!binaryFun!(pred)(rhs[0][j], lhs[j + k]))
|
||||
// not found
|
||||
return 0u;
|
||||
if(needles[i].empty)
|
||||
return i + 1;
|
||||
}
|
||||
// found!
|
||||
return 1u;
|
||||
}
|
||||
else
|
||||
|
||||
for(; !haystack.empty; haystack.popBack())
|
||||
{
|
||||
// Make one pass looking for empty ranges
|
||||
foreach (i, Unused; Ranges)
|
||||
foreach(i, Unused; Ranges)
|
||||
{
|
||||
// Empty range matches everything
|
||||
if (rhs[i].empty) return i + 1;
|
||||
}
|
||||
bool mismatch[Ranges.length];
|
||||
for (; !lhs.empty; lhs.popBack)
|
||||
{
|
||||
foreach (i, Unused; Ranges)
|
||||
static if(is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
|
||||
{
|
||||
if (mismatch[i]) continue;
|
||||
if (binaryFun!pred(lhs.back, rhs[i].back))
|
||||
// Single-element
|
||||
if(binaryFun!pred(haystack.back, needles[i]))
|
||||
{
|
||||
// Stay in the game
|
||||
rhs[i].popBack();
|
||||
// Done with success if exhausted
|
||||
if (rhs[i].empty) return i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Out
|
||||
mismatch[i] = true;
|
||||
// found, but continue to account for one-element
|
||||
// range matches (consider endsWith("ab", "b",
|
||||
// 'b') should return 1, not 2).
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(binaryFun!pred(haystack.back, needles[i].back))
|
||||
continue;
|
||||
}
|
||||
|
||||
// This code executed on failure to match
|
||||
// Out with this guy, check for the others
|
||||
uint result = endsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]);
|
||||
if(result > i)
|
||||
++result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// If execution reaches this point, then the back matches for all
|
||||
// needles ranges. What we need to do now is to lop off the back of
|
||||
// all ranges involved and recurse.
|
||||
foreach(i, Unused; Ranges)
|
||||
{
|
||||
static if(is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
|
||||
{
|
||||
// Test has passed in the previous loop
|
||||
return i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
needles[i].popBack();
|
||||
if(needles[i].empty)
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
debug(std_algorithm) scope(success)
|
||||
writeln("unittest @", __FILE__, ":", __LINE__, " done.");
|
||||
assert(endsWith("abc", ""));
|
||||
assert(!endsWith("abc", "a"));
|
||||
assert(!endsWith("abc", 'a'));
|
||||
assert(!endsWith("abc", "b"));
|
||||
assert(endsWith("abc", "a", "c") == 2);
|
||||
assert(endsWith("abc", 'a', 'c') == 2);
|
||||
assert(endsWith("abc", "c", "a") == 1);
|
||||
assert(endsWith("abc", "c", "c") == 1);
|
||||
assert(endsWith("abc", "x", "c", "b") == 2);
|
||||
assert(endsWith("abc", "x", "aa", "bc") == 3);
|
||||
assert(endsWith("abc", "x", "aaa", "sab") == 0);
|
||||
assert(endsWith("abc", "x", "aaa", "c", "sab") == 3);
|
||||
// string a = "abc";
|
||||
// immutable(char[1]) b = "c";
|
||||
// assert(wyda(a, b));
|
||||
}
|
||||
|
||||
/**
|
||||
Checks whether $(D doesThisEnd) starts with one of the individual
|
||||
elements $(D withOneOfThese) according to $(D pred).
|
||||
|
||||
Example:
|
||||
----
|
||||
assert(endsWith("abc", 'x', 'c', 'a') == 2);
|
||||
----
|
||||
*/
|
||||
uint endsWith(alias pred = "a == b", Range, Elements...)
|
||||
(Range doesThisEnd, Elements withOneOfThese)
|
||||
if (isInputRange!Range && Elements.length > 0
|
||||
&& is(typeof(binaryFun!pred(doesThisEnd.front, withOneOfThese[0]))))
|
||||
{
|
||||
if (doesThisEnd.empty) return 0;
|
||||
auto back = doesThisEnd.back;
|
||||
foreach (i, Unused; Elements)
|
||||
{
|
||||
if (binaryFun!pred(back, withOneOfThese[i])) return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
bool endsWith(alias pred = "a == b", R1, R2)
|
||||
(R1 doesThisEnd, R2 withThis)
|
||||
if(isInputRange!R1 &&
|
||||
isInputRange!R2 &&
|
||||
is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool))
|
||||
{
|
||||
alias doesThisEnd haystack;
|
||||
alias withThis needle;
|
||||
|
||||
// Special case for non-character arrays and strings of the same character type.
|
||||
static if(isArray!R1 && isArray!R2 &&
|
||||
((!isSomeString!R1 && !isSomeString!R2) ||
|
||||
(isSomeString!R1 && isSomeString!R2 &&
|
||||
is(Unqual!(typeof(haystack[0])) == Unqual!(typeof(needle[0]))))))
|
||||
{
|
||||
if(haystack.length < needle.length)
|
||||
return false;
|
||||
|
||||
immutable diff = haystack.length - needle.length;
|
||||
foreach(j; 0 .. needle.length)
|
||||
{
|
||||
if(!binaryFun!(pred)(needle[j], haystack[j + diff]))
|
||||
// not found
|
||||
return false;
|
||||
}
|
||||
|
||||
// found!
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
static if(hasLength!R1 && hasLength!R2)
|
||||
{
|
||||
if(haystack.length < needle.length)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(needle.empty)
|
||||
return true;
|
||||
|
||||
for(; !haystack.empty; haystack.popBack())
|
||||
{
|
||||
if(!binaryFun!pred(haystack.back, needle.back))
|
||||
break;
|
||||
|
||||
needle.popBack();
|
||||
|
||||
if(needle.empty)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
bool endsWith(alias pred = "a == b", R, E)
|
||||
(R doesThisEnd, E withThis)
|
||||
if(isInputRange!R &&
|
||||
is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool))
|
||||
{
|
||||
return doesThisEnd.empty ? false
|
||||
: binaryFun!pred(doesThisEnd.back, withThis);
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
debug(std_algorithm) scope(success)
|
||||
writeln("unittest @", __FILE__, ":", __LINE__, " done.");
|
||||
assert(!startsWith("abc", 'x', 'n', 'b'));
|
||||
assert(startsWith("abc", 'x', 'n', 'a') == 3);
|
||||
|
||||
//This is because we need to run some tests on ranges which _aren't_ arrays,
|
||||
//and as far as I can tell, all of the functions which would wrap an array
|
||||
//in a range (such as filter) don't return bidirectional ranges, so I'm
|
||||
//creating one here.
|
||||
auto static wrap(R)(R r)
|
||||
if(isBidirectionalRange!R)
|
||||
{
|
||||
struct Result
|
||||
{
|
||||
@property auto ref front() {return _range.front;}
|
||||
@property auto ref back() {return _range.back;}
|
||||
@property bool empty() {return _range.empty;}
|
||||
void popFront() {_range.popFront();}
|
||||
void popBack() {_range.popBack();}
|
||||
R _range;
|
||||
}
|
||||
|
||||
return Result(r);
|
||||
}
|
||||
|
||||
foreach(S; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
|
||||
{
|
||||
assert(!endsWith(to!S("abc"), 'a'));
|
||||
assert(endsWith(to!S("abc"), 'a', 'c') == 2);
|
||||
assert(!endsWith(to!S("abc"), 'x', 'n', 'b'));
|
||||
assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3);
|
||||
assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2);
|
||||
|
||||
foreach(T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
|
||||
{
|
||||
assert(endsWith(to!S("abc"), to!T("")));
|
||||
assert(!endsWith(to!S("abc"), to!T("a")));
|
||||
assert(!endsWith(to!S("abc"), to!T("b")));
|
||||
assert(!endsWith(to!S("abc"), to!T("bc"), 'c') == 1);
|
||||
assert(endsWith(to!S("abc"), to!T("a"), "c") == 2);
|
||||
assert(endsWith(to!S("abc"), to!T("c"), "a") == 1);
|
||||
assert(endsWith(to!S("abc"), to!T("c"), "c") == 1);
|
||||
assert(endsWith(to!S("abc"), to!T("x"), 'c', "b") == 2);
|
||||
assert(endsWith(to!S("abc"), 'x', to!T("aa"), "bc") == 3);
|
||||
assert(endsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0);
|
||||
assert(endsWith(to!S("abc"), to!T("x"), "aaa", "c", "sab") == 3);
|
||||
assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")));
|
||||
assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], cast(int[])null));
|
||||
assert(!endsWith([0, 1, 2, 3, 4, 5], 0));
|
||||
assert(!endsWith([0, 1, 2, 3, 4, 5], 4));
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], 5));
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], 0, 4, 5) == 3);
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], [5]));
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], [4, 5]));
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], [4, 5], 7) == 1);
|
||||
assert(!endsWith([0, 1, 2, 3, 4, 5], [2, 4, 5]));
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], [2, 4, 5], [3, 4, 5]) == 2);
|
||||
|
||||
assert(!endsWith(wrap([0, 1, 2, 3, 4, 5]), 4));
|
||||
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), 5));
|
||||
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [5]));
|
||||
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [4, 5]));
|
||||
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [4, 5], 7) == 1);
|
||||
assert(!endsWith(wrap([0, 1, 2, 3, 4, 5]), [2, 4, 5]));
|
||||
assert(endsWith(wrap([0, 1, 2, 3, 4, 5]), [2, 4, 5], [3, 4, 5]) == 2);
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], wrap([4, 5])));
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], wrap([4, 5]), 7));
|
||||
assert(!endsWith([0, 1, 2, 3, 4, 5], wrap([2, 4, 5])));
|
||||
assert(endsWith([0, 1, 2, 3, 4, 5], [2, 4, 5], wrap([3, 4, 5])) == 2);
|
||||
}
|
||||
|
||||
// findAdjacent
|
||||
|
|
|
@ -1363,7 +1363,7 @@ C[] chomp(C)(C[] s)
|
|||
}
|
||||
|
||||
/// Ditto
|
||||
C[] chomp(C, C1)(C[] s, in C1[] delimiter)
|
||||
C[] chomp(C, C1)(C[] s, const(C1)[] delimiter)
|
||||
{
|
||||
if (endsWith(s, delimiter))
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue