Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions std/algorithm.d
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
182 changes: 182 additions & 0 deletions std/range.d
Original file line number Diff line number Diff line change
Expand Up @@ -2478,6 +2478,188 @@ 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;

See_Also:
$(XREF algorithm, until)

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think takeWhile and dropWhile should be in std.algorithm. All functions in std.range either modify a range by its structure (take, chain, zip, ...), or generates a range (recurrence, iota, ...), but none of them actually modify a range by its values.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. Maybe. But with take being in std.range, I'm ill-inclined to put takeWhile in std.algorithm. Also, drop definitely fits better in std.range, and I'm ill-inclined to have dropWhile in a separate module for the same reason.

Also, takeWhile and dropWhile don't modify a range anymore than take or chain do. I don't understand the distinction that you're making.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmdavis: The difference is that takeWhile and dropWhile take predicates. Anyway, this is just an observation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it is true that for the most part the range-based functions which take predicates are in std.algorithm rather than std.range, but there are already some in there (albeit not many). But really, what should go in std.range or std.algorithm is a bit debatable. They're not really clearly delineated. I could easily see takeWhile and dropWhile in std.algorithm (and there are already similar functions there - e.g. until), but I'd be more inclined to put them in std.range, because take and drop are there. It could go either way though.

if(isInputRange!R &&
is(typeof(unaryFun!pred(range.front))))
{
struct Result
{
@property bool empty()
{
return _done;
}

@property 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);
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)));
static assert(!isBidirectionalRange!(typeof(r)));
static assert(!isRandomAccessRange!(typeof(r)));
static assert(!isOutputRange!(typeof(r), ElementType!(typeof(r))));
}


/++
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 $(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)
if(isInputRange!R)
{
popFrontN(range, n);
return range;
}

//Verify Examples
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
{
assert(drop("", 5).empty);
assert(equal(drop(filter!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3]));
}


/++
Pops elements off of the given range until $(D pred) fails. 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);

dchar max = 'q';
bool del(dchar c)
{
return c < max;
}
assert(equal(dropWhile!del("hello world"), "world"));
}


/**
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
Expand Down