From 1b7db2de5eba6905ca3ac7deda236a5221b60f3d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Nov 2012 19:20:39 +0100 Subject: [PATCH 1/6] improvements/fixes for count countUntil Changes: 1. Count: 1.1 Better conditional statement 1.2 Count will now turn down any infinite haystacks (or else you'd count to infinity...) 1.3 Infinite needle will instantaneously return a count of 0. 1.4 Fixes a condition that was checking pred(haystack, needle), as opposed to pred(haystack.front, needle.front) (guess no one ever tried a fuzzy count?) 1.5 Fixes reference range support...* 2. countUntil: 2.1 Better conditional statement 2.2 countUntil supports InputRange for element and predicate search. 2.3 Fixes unicode bug (return the amount of popFronts, not index) 2.4 Fixes http://d.puremagic.com/issues/show_bug.cgi?id=8804 2.5 Fixes http://d.puremagic.com/issues/show_bug.cgi?id=8821 Note: count is still subject to the bugs in splitAfter * Incorrect reference range support * Incorrect split for forward range Both are corrected in another pull. As such, no unittests are added (yet). --- std/algorithm.d | 151 +++++++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/std/algorithm.d b/std/algorithm.d index e8c5b3506..8256954bb 100644 --- a/std/algorithm.d +++ b/std/algorithm.d @@ -4006,35 +4006,34 @@ unittest assert(countUntil("hello world", "world") == 6); assert(countUntil("hello world", 'r') == 8); assert(countUntil("hello world", "programming") == -1); +assert(countUntil("日本語", "本語") == 1); +assert(countUntil("日本語", '語') == 2); +assert(countUntil("日本語", "五") == -1); +assert(countUntil("日本語", '五') == -1); assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2); assert(countUntil([0, 7, 12, 22, 9], 9) == 4); assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3); -------------------- +/ -ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) -if (is(typeof(startsWith!pred(haystack, needle)))) +ptrdiff_t countUntil(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) + if (isForwardRange!R1 && isForwardRange!R2 && + is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { - static if (isNarrowString!R) - { - // Narrow strings are handled a bit differently - auto length = haystack.length; - for (; !haystack.empty; haystack.popFront()) - { - if (startsWith!pred(haystack, needle)) - { - return length - haystack.length; - } - } - } - else - { - typeof(return) result; - for (; !haystack.empty; ++result, haystack.popFront()) - { - if (startsWith!pred(haystack, needle)) return result; - } - } - return -1; + //Note: This implementation does take narrow strings into consideration + typeof(return) result; + for (; !haystack.empty; ++result, haystack.popFront()) + if (startsWith!pred(haystack.save, needle.save)) return result; + + static if (isInfinite!R1) assert(0); // @@@8804@@@ + else return -1; +} +/// ditto +ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) + if (isInputRange!R && + is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) +{ + bool pred2(ElementType!R a) { return binaryFun!pred(a, needle); } + return countUntil!pred2(haystack); } //Verify Examples. @@ -4043,6 +4042,10 @@ unittest assert(countUntil("hello world", "world") == 6); assert(countUntil("hello world", 'r') == 8); assert(countUntil("hello world", "programming") == -1); + assert(countUntil("日本語", "本語") == 1); + assert(countUntil("日本語", '語') == 2); + assert(countUntil("日本語", "五") == -1); + assert(countUntil("日本語", '五') == -1); assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2); assert(countUntil([0, 7, 12, 22, 9], 9) == 4); assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3); @@ -4060,29 +4063,32 @@ assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3); -------------------- +/ ptrdiff_t countUntil(alias pred, R)(R haystack) -if (isForwardRange!R && is(typeof(unaryFun!pred(haystack.front)) == bool)) + if (isInputRange!R && + is(typeof(unaryFun!pred(haystack.front)) : bool)) { - static if (isNarrowString!R) + typeof(return) i; + static if (isRandomAccessRange!R) { - // Narrow strings are handled a bit differently - auto length = haystack.length; - for (; !haystack.empty; haystack.popFront()) + static if (hasLength!R) { - if (unaryFun!pred(haystack.front)) - { - return length - haystack.length; - } + auto len = cast(typeof(return)) haystack.length; + for ( ; i < len ; ++i) + if (unaryFun!pred(haystack[i])) return i; + } + else //if (isInfinite!R) + { + for ( ; ; ++i ) + if (unaryFun!pred(haystack[i])) return i; } } - else + else //everything else (including narrow strings) { - typeof(return) result; - for (; !haystack.empty; ++result, haystack.popFront()) - { - if (unaryFun!pred(haystack.front)) return result; - } + for ( ; !haystack.empty; ++i, haystack.popFront()) + if (unaryFun!pred(haystack.front)) return i; } - return -1; + + static if (isInfinite!R) assert(0); // @@@8804@@@ + else return -1; } //Verify Examples. @@ -4092,6 +4098,32 @@ unittest assert(countUntil!(std.ascii.isDigit)("hello world") == -1); assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3); } +unittest +{ + // References + { + // input + ReferenceInputRange!int r; + r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]); + assert(r.countUntil(3) == 3); + r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]); + assert(r.countUntil(7) == -1); + } + { + // forward + auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]); + assert(r.save.countUntil([3, 4]) == 3); + assert(r.save.countUntil(3) == 3); + assert(r.save.countUntil([3, 7]) == -1); + assert(r.save.countUntil(7) == -1); + } + { + // infinite forward + auto r = new ReferenceInfiniteForwardRange!int(0); + assert(r.save.countUntil([3, 4]) == 3); + assert(r.save.countUntil(3) == 3); + } +} /** * $(RED Deprecated. It will be removed in January 2013. @@ -4952,6 +4984,9 @@ $(D 2). The third version counts the elements for which $(D pred(x)) is $(D true). Performs $(BIGOH r.length) evaluations of $(D pred). +Note: Regardless of the version, $(D count) will not accept infinite ranges +as a $(D haystack). + Example: ---- // count elements in range @@ -4962,15 +4997,18 @@ assert(count!("a > b")(a, 2) == 5); assert(count("abcadfabf", "ab") == 2); assert(count("ababab", "abab") == 1); assert(count("ababab", "abx") == 0); +// fuzzy count range in range +assert(count!"std.uni.toLower(a) == std.uni.toLower(b)"("AbcAdFaBf", "ab") == 2); // count predicate in range assert(count!("a > 1")(a) == 8); ---- */ -size_t count(alias pred = "a == b", Range, E)(Range r, E value) -if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, value)) == bool)) +size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) + if (isInputRange!Range && !isInfinite!Range && + is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) { - bool pred2(ElementType!(Range) a) { return binaryFun!pred(a, value); } - return count!(pred2)(r); + bool pred2(ElementType!Range a) { return binaryFun!pred(a, needle); } + return count!pred2(haystack); } unittest @@ -5001,14 +5039,20 @@ unittest /// Ditto size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) -if (isInputRange!R1 && isForwardRange!R2 && is(typeof(binaryFun!pred(haystack, needle)) == bool)) + if (isForwardRange!R1 && !isInfinite!R1 && + isForwardRange!R2 && + is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { enforce(!needle.empty, "Cannot count occurrences of an empty range"); - size_t result; - for (; findSkip!pred(haystack, needle); ++result) + static if (isInfinite!R2) + return 0; + else { + size_t result; + for (; findSkip!pred(haystack, needle.save); ++result) + {} + return result; } - return result; } unittest @@ -5016,16 +5060,18 @@ unittest assert(count("abcadfabf", "ab") == 2); assert(count("ababab", "abab") == 1); assert(count("ababab", "abx") == 0); + assert(count!"std.uni.toLower(a) == std.uni.toLower(b)"("AbcAdFaBf", "ab") == 2); } /// Ditto -size_t count(alias pred = "true", Range)(Range r) if (isInputRange!(Range)) +size_t count(alias pred = "true", R)(R haystack) + if (isInputRange!R && !isInfinite!R && + is(typeof(unaryFun!pred(haystack.front)) : bool)) { size_t result; - for (; !r.empty; r.popFront()) - { - if (unaryFun!pred(r.front)) ++result; - } + alias ElementType!R T; //For narrow strings + foreach (T elem; haystack) + if (unaryFun!pred(elem)) ++result; return result; } @@ -5035,6 +5081,7 @@ unittest writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; assert(count!("a == 3")(a) == 2); + assert(count("日本語") == 3); } // balancedParens From 3dffa6c47513aa2b4b47d20c96c4d42f2047a8d0 Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Mon, 19 Nov 2012 12:06:09 +0100 Subject: [PATCH 2/6] more improvements in cout/countUntil --- std/algorithm.d | 54 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/std/algorithm.d b/std/algorithm.d index 8256954bb..68b4d2498 100644 --- a/std/algorithm.d +++ b/std/algorithm.d @@ -4019,12 +4019,23 @@ ptrdiff_t countUntil(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2 && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { - //Note: This implementation does take narrow strings into consideration typeof(return) result; - for (; !haystack.empty; ++result, haystack.popFront()) - if (startsWith!pred(haystack.save, needle.save)) return result; - - static if (isInfinite!R1) assert(0); // @@@8804@@@ + static if (hasLength!R1) //Note: String don't have length + { + //Delegate to find. Find is very efficient + //We save haystack, but we don't care for needle + auto r2 = find!pred(haystack.save, needle); + if (!r2.empty) return cast(typeof(return)) (haystack.length - r2.length); + } + else + { + //Default case, slower route doing startsWith iteration + for (; !haystack.empty; ++result, haystack.popFront()) + if (startsWith!pred(haystack.save, needle.save)) return result; + } + + //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement" + static if (isInfinite!R1) assert(0); else return -1; } /// ditto @@ -4069,6 +4080,8 @@ ptrdiff_t countUntil(alias pred, R)(R haystack) typeof(return) i; static if (isRandomAccessRange!R) { + //Optimized RA implementation. Since we want to count *and* iterate at + //the same time, it is more efficient this way. static if (hasLength!R) { auto len = cast(typeof(return)) haystack.length; @@ -4081,13 +4094,25 @@ ptrdiff_t countUntil(alias pred, R)(R haystack) if (unaryFun!pred(haystack[i])) return i; } } - else //everything else (including narrow strings) + else static if (hasLength!R) { - for ( ; !haystack.empty; ++i, haystack.popFront()) - if (unaryFun!pred(haystack.front)) return i; + //For those odd ranges that have a length, but aren't RA. + //It is faster to quick find, and then compare the lengths + auto r2 = find!pred(haystack.save); + if (!r2.empty) return cast(typeof(return)) (haystack.length - r2.length); } - - static if (isInfinite!R) assert(0); // @@@8804@@@ + else //Everything else + { + alias ElementType!R T; //For narrow strings forces dchar iteration + foreach (T elem; haystack) + { + if (unaryFun!pred(elem)) return i; + ++i; + } + } + + //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement" + static if (isInfinite!R) assert(0); else return -1; } @@ -5045,11 +5070,16 @@ size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) { enforce(!needle.empty, "Cannot count occurrences of an empty range"); static if (isInfinite!R2) + { + //Note: This is the special case of looking for an infinite inside a finite... + //"How many instances of the Fibonacci sequence can you count in [1, 2, 3]?" - "None." return 0; + } else { size_t result; - for (; findSkip!pred(haystack, needle.save); ++result) + //Note: haystack is not saved, because findskip is designed to modify it + for ( ; findSkip!pred(haystack, needle.save) ; ++result) {} return result; } @@ -5069,7 +5099,7 @@ size_t count(alias pred = "true", R)(R haystack) is(typeof(unaryFun!pred(haystack.front)) : bool)) { size_t result; - alias ElementType!R T; //For narrow strings + alias ElementType!R T; //For narrow strings forces dchar iteration foreach (T elem; haystack) if (unaryFun!pred(elem)) ++result; return result; From a98a93f1e4bbc33a6decb6f6ab1118d4e240068a Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Fri, 16 Nov 2012 13:46:08 +0100 Subject: [PATCH 3/6] fixing countUntil vs indexOf issue --- std/path.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/path.d b/std/path.d index 60620371f..04e2848be 100644 --- a/std/path.d +++ b/std/path.d @@ -2682,7 +2682,7 @@ string expandTilde(string inputPath) // Extract username, searching for path separator. string username; - auto last_char = std.algorithm.countUntil(path, dirSeparator[0]); + auto last_char = std.string.indexOf(path, dirSeparator[0]); if (last_char == -1) { From 0a3b7be8e135e35c5a2d9ee4d665f769b6a66150 Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Sat, 17 Nov 2012 10:27:01 +0100 Subject: [PATCH 4/6] Quick splitter countUntil fix This is more of a band-aid..., splitter needs some deep fixing anyways... --- std/algorithm.d | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/std/algorithm.d b/std/algorithm.d index 68b4d2498..bce1ca425 100644 --- a/std/algorithm.d +++ b/std/algorithm.d @@ -1924,7 +1924,7 @@ assert(equal(splitter(a, 0), [ [], [1] ])); */ auto splitter(Range, Separator)(Range r, Separator s) if (is(typeof(ElementType!Range.init == Separator.init)) - && (hasSlicing!Range || isNarrowString!Range)) + && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range)) { static struct Result { @@ -1941,8 +1941,8 @@ if (is(typeof(ElementType!Range.init == Separator.init)) { static IndexType lastIndexOf(Range haystack, Separator needle) { - immutable index = countUntil(retro(haystack), needle); - return (index == -1) ? -1 : haystack.length - 1 - index; + auto r = haystack.retro().find(needle); + return r.retro().length - 1; } } @@ -1970,8 +1970,8 @@ if (is(typeof(ElementType!Range.init == Separator.init)) assert(!empty); if (_frontLength == _unComputed) { - _frontLength = countUntil(_input, _separator); - if (_frontLength == -1) _frontLength = _input.length; + auto r = _input.find(_separator); + _frontLength = _input.length - r.length; } return _input[0 .. _frontLength]; } From 076c853b7389f8c81e0fa8130e0e0db073d38bad Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Sat, 17 Nov 2012 11:13:27 +0100 Subject: [PATCH 5/6] indexOf: More details Not 100% about the text... --- std/algorithm.d | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/std/algorithm.d b/std/algorithm.d index bce1ca425..ce930e324 100644 --- a/std/algorithm.d +++ b/std/algorithm.d @@ -4152,11 +4152,22 @@ unittest /** * $(RED Deprecated. It will be removed in January 2013. - * Please use $(LREF countUntil) instead.) + * Currently defaults to $(LREF countUntil) instead.) * - * Same as $(D countUntil). This symbol has been deprecated - * because it is easily confused with the homonym function + * Not to be confused with it's homonym function * in $(D std.string). + * + * Please use $(D std.string.indexOf) if you wish to find + * the index of a character in a string. + * + * Otherwise, please use $(D std.string.countUntil) to find + * an element's logical position in a range. + * + * Example: + * -------- + * assert(std.string.indexOf("日本語", '本') == 3); + * assert(std.algorithm.countUntil("日本語", '本') == 1); + * -------- */ deprecated ptrdiff_t indexOf(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (is(typeof(startsWith!pred(haystack, needle)))) From 20d77cb8ffd60fe64dd044b58b9b5a54de29bbe2 Mon Sep 17 00:00:00 2001 From: monarch dodra Date: Thu, 22 Nov 2012 14:15:19 +0100 Subject: [PATCH 6/6] Changes to count[Until] as suggested by review --- std/algorithm.d | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/std/algorithm.d b/std/algorithm.d index ce930e324..967bc698c 100644 --- a/std/algorithm.d +++ b/std/algorithm.d @@ -4020,7 +4020,7 @@ ptrdiff_t countUntil(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { typeof(return) result; - static if (hasLength!R1) //Note: String don't have length + static if (hasLength!R1) //Note: Narrow strings don't have length. { //Delegate to find. Find is very efficient //We save haystack, but we don't care for needle @@ -4030,7 +4030,7 @@ ptrdiff_t countUntil(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) else { //Default case, slower route doing startsWith iteration - for (; !haystack.empty; ++result, haystack.popFront()) + for ( ; !haystack.empty ; ++result, haystack.popFront() ) if (startsWith!pred(haystack.save, needle.save)) return result; } @@ -4084,8 +4084,8 @@ ptrdiff_t countUntil(alias pred, R)(R haystack) //the same time, it is more efficient this way. static if (hasLength!R) { - auto len = cast(typeof(return)) haystack.length; - for ( ; i < len ; ++i) + immutable len = cast(typeof(return)) haystack.length; + for ( ; i < len ; ++i ) if (unaryFun!pred(haystack[i])) return i; } else //if (isInfinite!R) @@ -4154,7 +4154,7 @@ unittest * $(RED Deprecated. It will be removed in January 2013. * Currently defaults to $(LREF countUntil) instead.) * - * Not to be confused with it's homonym function + * Not to be confused with its homonym function * in $(D std.string). * * Please use $(D std.string.indexOf) if you wish to find @@ -5020,8 +5020,8 @@ $(D 2). The third version counts the elements for which $(D pred(x)) is $(D true). Performs $(BIGOH r.length) evaluations of $(D pred). -Note: Regardless of the version, $(D count) will not accept infinite ranges -as a $(D haystack). +Note: Regardless of the overload, $(D count) will not accept +infinite ranges for $(D haystack). Example: ----