From ea576d06d9e88d4c37416c9496b36bb6213cca27 Mon Sep 17 00:00:00 2001 From: jmdavis Date: Sat, 16 Jul 2011 18:21:33 -0700 Subject: [PATCH 1/8] Implemented takeWhile. http://d.puremagic.com/issues/show_bug.cgi?id=4535 --- std/range.d | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/std/range.d b/std/range.d index 622f7b8be66..c5d65f87e08 100644 --- a/std/range.d +++ b/std/range.d @@ -2478,6 +2478,87 @@ unittest assert(s.empty); } + +/++ + Returns a range which lazily iterates over the given range until $(D pred) + is $(D false) for $(D r.front) or $(D r) is empty; + + Examples: +-------------------- +assert(equal(takeWhile!"a < 3"([0, 2, 1, 5, 0, 3]), [0, 2, 1])); +assert(equal(takeWhile!(std.uni.isAlpha)("hello world"), "hello")); +assert(equal(takeWhile!(not!(std.ascii.isDigit))("hello world"), "hello world")); +assert(takeWhile!(std.uni.isWhite)("hello world").empty); +-------------------- + +/ +auto takeWhile(alias pred, R)(R range) + if(isInputRange!R && + is(typeof(unaryFun!pred(range.front)))) +{ + struct Result + { + bool empty() + { + return _done; + } + + auto ref front() + { + assert(!_done); + return _input.front; + } + + void popFront() + { + _input.popFront(); + + if(_input.empty || !unaryFun!pred(_input.front)) + _done = true; + } + + static if(isForwardRange!R) + { + @property auto save() + { + return Result(_input); + } + } + + this(R input) + { + _input = input; + _done = _input.empty || !unaryFun!pred(_input.front); + } + + bool _done; + R _input; + } + + return Result(range); +} + +//Verify Examples. +unittest +{ + assert(equal(takeWhile!"a < 3"([0, 2, 1, 5, 0, 3]), [0, 2, 1])); + assert(equal(takeWhile!(std.uni.isAlpha)("hello world"), "hello")); + assert(equal(takeWhile!(not!(std.ascii.isDigit))("hello world"), "hello world")); + assert(takeWhile!(std.uni.isWhite)("hello world").empty); +} + +unittest +{ + assert(takeWhile!(std.uni.isWhite)("").empty); + + auto r = takeWhile!(std.uni.isAlpha)("hello world"); + static assert(isInputRange!(typeof(r))); + static assert(isForwardRange!(typeof(r))); + static assert(!isBidirectionalRange!(typeof(r))); + static assert(!isRandomAccessRange!(typeof(r))); + static assert(!isOutputRange!(typeof(r), ElementType!(typeof(r)))); +} + + /** Eagerly advances $(D r) itself (not a copy) $(D n) times (by calling $(D r.popFront) at most $(D n) times). The pass of $(D r) into $(D From 406e4d6dea30ac55d360660ceebddd10b95ebd40 Mon Sep 17 00:00:00 2001 From: jmdavis Date: Sat, 16 Jul 2011 19:29:56 -0700 Subject: [PATCH 2/8] Implemented drop. This partially fulfills http://d.puremagic.com/issues/show_bug.cgi?id=5645 I'm not sure if it makes any sense to implement the slice function asked for in there though. But then again, I also don't quite understand what it does either. Those two items probably should have been on separate enhancement requests. --- std/range.d | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/std/range.d b/std/range.d index c5d65f87e08..35000eeb57b 100644 --- a/std/range.d +++ b/std/range.d @@ -2559,6 +2559,52 @@ unittest } +/++ + Pops $(D n) elements off of the given range and returns it. The original + range is not affected. If the length of the given range is less than $(D n) + than an empty range is returned. + + The main reason to use this instead of $(LREF popFrontN) is so that you can + pop the elements off and pass the resulting range in one operation, allowing + for a more functional style of programming. + + Examples: +-------------------- +auto r = "hello world"; +assert(drop(r, 3) == "lo world"); +assert(r == "hello world"); + +assert(drop([0, 2, 1, 5, 0, 3], 3) == [5, 0, 3]); +assert(drop("hello world", 6) == "world"); +assert(drop("hello world", 50).empty); +-------------------- + +/ +R drop(R)(R range, size_t n) + if(isForwardRange!R) +{ + auto r = range.save; + r.popFrontN(n); + return r; +} + +//Verify Examples +unittest +{ + auto r = "hello world"; + assert(drop(r, 3) == "lo world"); + assert(r == "hello world"); + + assert(drop([0, 2, 1, 5, 0, 3], 3) == [5, 0, 3]); + assert(drop("hello world", 6) == "world"); + assert(drop("hello world", 50).empty); +} + +unittest +{ + assert(drop("", 5).empty); +} + + /** Eagerly advances $(D r) itself (not a copy) $(D n) times (by calling $(D r.popFront) at most $(D n) times). The pass of $(D r) into $(D From a5b5edaffd63e4aff7b83bf139f3cb9babcf2c9f Mon Sep 17 00:00:00 2001 From: jmdavis Date: Sat, 16 Jul 2011 19:57:55 -0700 Subject: [PATCH 3/8] Added dropWhile. --- std/range.d | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/std/range.d b/std/range.d index 35000eeb57b..7b28f50cea2 100644 --- a/std/range.d +++ b/std/range.d @@ -2605,6 +2605,43 @@ unittest } +/++ + Pops elements off of the given range until $(D pred) fails. Then the + resulting range is returned. + + Examples: +-------------------- +assert(dropWhile!"a < 3"([0, 2, 1, 5, 0, 3]), [5, 0, 3])); +assert(dropWhile!(std.uni.isAlpha)("hello world"), " world")); +assert(dropWhile!(not!(std.ascii.isDigit))("hello world").empty); +assert(dropWhile!(std.uni.isWhite)("hello world") == "hello world"); +-------------------- + +/ +R dropWhile(alias pred, R)(R range) + if(isInputRange!R && + is(typeof(unaryFun!pred(range.front)))) +{ + while(!range.empty && unaryFun!pred(range.front)) + range.popFront(); + + return range; +} + +//Verify Examples. +unittest +{ + assert(dropWhile!"a < 3"([0, 2, 1, 5, 0, 3]) == [5, 0, 3]); + assert(dropWhile!(std.uni.isAlpha)("hello world") == " world"); + assert(dropWhile!(not!(std.ascii.isDigit))("hello world").empty); + assert(dropWhile!(std.uni.isWhite)("hello world") == "hello world"); +} + +unittest +{ + assert(dropWhile!(std.uni.isWhite)("").empty); +} + + /** Eagerly advances $(D r) itself (not a copy) $(D n) times (by calling $(D r.popFront) at most $(D n) times). The pass of $(D r) into $(D From 8cea2e5851f0ddb9b8a753a00e6a16f13bafd7d8 Mon Sep 17 00:00:00 2001 From: jmdavis Date: Sat, 16 Jul 2011 20:06:44 -0700 Subject: [PATCH 4/8] Some adjustments to std.range.drop. --- std/range.d | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/std/range.d b/std/range.d index 7b28f50cea2..35412826d69 100644 --- a/std/range.d +++ b/std/range.d @@ -2560,9 +2560,8 @@ unittest /++ - Pops $(D n) elements off of the given range and returns it. The original - range is not affected. If the length of the given range is less than $(D n) - than an empty range is returned. + Pops $(D n) elements off of the given range and returns it. If the length of + the given range is less than $(D n) than an empty range is returned. The main reason to use this instead of $(LREF popFrontN) is so that you can pop the elements off and pass the resulting range in one operation, allowing @@ -2570,30 +2569,21 @@ unittest Examples: -------------------- -auto r = "hello world"; -assert(drop(r, 3) == "lo world"); -assert(r == "hello world"); - assert(drop([0, 2, 1, 5, 0, 3], 3) == [5, 0, 3]); assert(drop("hello world", 6) == "world"); assert(drop("hello world", 50).empty); -------------------- +/ R drop(R)(R range, size_t n) - if(isForwardRange!R) + if(isInputRange!R) { - auto r = range.save; - r.popFrontN(n); - return r; + range.popFrontN(n); + return range; } //Verify Examples unittest { - auto r = "hello world"; - assert(drop(r, 3) == "lo world"); - assert(r == "hello world"); - assert(drop([0, 2, 1, 5, 0, 3], 3) == [5, 0, 3]); assert(drop("hello world", 6) == "world"); assert(drop("hello world", 50).empty); From bcd2cfd872bd476135e2e89ee9076ce0a956a314 Mon Sep 17 00:00:00 2001 From: jmdavis Date: Sat, 16 Jul 2011 20:19:53 -0700 Subject: [PATCH 5/8] Made std.range.drop's Result struct static. --- std/range.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/range.d b/std/range.d index 35412826d69..439e385a749 100644 --- a/std/range.d +++ b/std/range.d @@ -2495,7 +2495,7 @@ auto takeWhile(alias pred, R)(R range) if(isInputRange!R && is(typeof(unaryFun!pred(range.front)))) { - struct Result + static struct Result { bool empty() { From 911452895603836648b592f39c5e1e2b4e310e19 Mon Sep 17 00:00:00 2001 From: jmdavis Date: Sun, 17 Jul 2011 01:17:04 -0700 Subject: [PATCH 6/8] Fixed some items from review comments. --- std/range.d | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/std/range.d b/std/range.d index 439e385a749..47454b78f62 100644 --- a/std/range.d +++ b/std/range.d @@ -2497,12 +2497,12 @@ auto takeWhile(alias pred, R)(R range) { static struct Result { - bool empty() + @property bool empty() { return _done; } - auto ref front() + @property auto ref front() { assert(!_done); return _input.front; @@ -2549,6 +2549,7 @@ unittest unittest { assert(takeWhile!(std.uni.isWhite)("").empty); + assert(equal(takeWhile!"a < 3"(filter!"true"([0, 2, 1, 5, 0, 3])), [0, 2, 1])); auto r = takeWhile!(std.uni.isAlpha)("hello world"); static assert(isInputRange!(typeof(r))); @@ -2577,7 +2578,7 @@ assert(drop("hello world", 50).empty); R drop(R)(R range, size_t n) if(isInputRange!R) { - range.popFrontN(n); + popFrontN(range, n); return range; } @@ -2592,6 +2593,7 @@ unittest unittest { assert(drop("", 5).empty); + assert(equal(drop(filter!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3])); } From bbed3d2bca454cd37824f125560e753377c7fe3a Mon Sep 17 00:00:00 2001 From: jmdavis Date: Thu, 28 Jul 2011 01:00:47 -0700 Subject: [PATCH 7/8] Added links between takeWhile and until in documentation. --- std/algorithm.d | 3 +++ std/range.d | 3 +++ 2 files changed, 6 insertions(+) diff --git a/std/algorithm.d b/std/algorithm.d index 361003eaa99..600825d1731 100644 --- a/std/algorithm.d +++ b/std/algorithm.d @@ -3658,6 +3658,9 @@ enum OpenRight Lazily iterates $(D range) until value $(D sentinel) is found, at which point it stops. +See_Also: + $(XREF range, takeWhile) + Example: ---- int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5]; diff --git a/std/range.d b/std/range.d index 47454b78f62..d7c02fb6bb3 100644 --- a/std/range.d +++ b/std/range.d @@ -2483,6 +2483,9 @@ unittest Returns a range which lazily iterates over the given range until $(D pred) is $(D false) for $(D r.front) or $(D r) is empty; + See_Also: + $(XREF algorithm, until) + Examples: -------------------- assert(equal(takeWhile!"a < 3"([0, 2, 1, 5, 0, 3]), [0, 2, 1])); From ec47424805806fd6269ce10637a1a1ba55927c8b Mon Sep 17 00:00:00 2001 From: jmdavis Date: Fri, 29 Jul 2011 20:19:54 -0700 Subject: [PATCH 8/8] Some improvements to drop's documentation. I also made it so that takeWhile's Result isn't static anymore. It didn't work with delegates that way. --- std/range.d | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/std/range.d b/std/range.d index d7c02fb6bb3..017dd36ccbf 100644 --- a/std/range.d +++ b/std/range.d @@ -2498,7 +2498,7 @@ auto takeWhile(alias pred, R)(R range) if(isInputRange!R && is(typeof(unaryFun!pred(range.front)))) { - static struct Result + struct Result { @property bool empty() { @@ -2554,6 +2554,13 @@ unittest assert(takeWhile!(std.uni.isWhite)("").empty); assert(equal(takeWhile!"a < 3"(filter!"true"([0, 2, 1, 5, 0, 3])), [0, 2, 1])); + dchar max = 'q'; + bool del(dchar c) + { + return c < max; + } + assert(equal(takeWhile!del("hello world"), "hello ")); + auto r = takeWhile!(std.uni.isAlpha)("hello world"); static assert(isInputRange!(typeof(r))); static assert(isForwardRange!(typeof(r))); @@ -2567,15 +2574,23 @@ unittest Pops $(D n) elements off of the given range and returns it. If the length of the given range is less than $(D n) than an empty range is returned. - The main reason to use this instead of $(LREF popFrontN) is so that you can - pop the elements off and pass the resulting range in one operation, allowing - for a more functional style of programming. + The main reason to use $(D drop) instead of $(LREF popFrontN) is so that you + can pop the elements off and pass the resulting range in one operation, + allowing for a more functional style of programming. However, this means + that you don't know exactly how many elements were actually popped in the + case where the range had fewer than $(D n) elements (unless you got its + length first, which isn't an effcient thing to do for all ranges). Also, + because it doesn't take the range by reference (unlike $(LREF popFrontN)), + you can pass the results of other functions to it directly. But that also + means that it does not affect the original range as long as it's a value + type. Examples: -------------------- assert(drop([0, 2, 1, 5, 0, 3], 3) == [5, 0, 3]); assert(drop("hello world", 6) == "world"); assert(drop("hello world", 50).empty); +assert(equal(drop(take("hello world", 6), 3), "lo ")); -------------------- +/ R drop(R)(R range, size_t n) @@ -2591,6 +2606,7 @@ unittest assert(drop([0, 2, 1, 5, 0, 3], 3) == [5, 0, 3]); assert(drop("hello world", 6) == "world"); assert(drop("hello world", 50).empty); + assert(equal(drop(take("hello world", 6), 3), "lo ")); } unittest @@ -2601,8 +2617,8 @@ unittest /++ - Pops elements off of the given range until $(D pred) fails. Then the - resulting range is returned. + Pops elements off of the given range until $(D pred) fails. The resulting + range is returned. Examples: -------------------- @@ -2634,6 +2650,13 @@ unittest unittest { assert(dropWhile!(std.uni.isWhite)("").empty); + + dchar max = 'q'; + bool del(dchar c) + { + return c < max; + } + assert(equal(dropWhile!del("hello world"), "world")); }