Move each() to std.algorithm.iteration.

This commit is contained in:
H. S. Teoh 2015-01-17 22:05:55 -08:00
parent 5cae4f0cb5
commit fa803294a6
2 changed files with 144 additions and 141 deletions

View file

@ -676,6 +676,150 @@ unittest
auto m = immutable(S).init.repeat().map!"a".save;
}
// each
/**
Eagerly iterates over $(D r) and calls $(D pred) over _each element.
Params:
pred = predicate to apply to each element of the range
r = range or iterable over which each iterates
Example:
---
void deleteOldBackups()
{
import std.algorithm, std.datetime, std.file;
auto cutoff = Clock.currTime() - 7.days;
dirEntries("", "*~", SpanMode.depth)
.filter!(de => de.timeLastModified < cutoff)
.each!remove();
}
---
If the range supports it, the value can be mutated in place. Examples:
---
arr.each!((ref a) => a++);
arr.each!"a++";
---
If no predicate is specified, $(D each) will default to doing nothing
but consuming the entire range. $(D .front) will be evaluated, but this
can be avoided by explicitly specifying a predicate lambda with a
$(D lazy) parameter.
$(D each) also supports $(D opApply)-based iterators, so it will work
with e.g. $(XREF parallelism, parallel).
See_Also: $(XREF range,tee)
*/
template each(alias pred = "a")
{
import std.typetuple : TypeTuple;
alias BinaryArgs = TypeTuple!(pred, "i", "a");
enum isRangeUnaryIterable(R) =
is(typeof(unaryFun!pred(R.init.front)));
enum isRangeBinaryIterable(R) =
is(typeof(binaryFun!BinaryArgs(0, R.init.front)));
enum isRangeIterable(R) =
isInputRange!R &&
(isRangeUnaryIterable!R || isRangeBinaryIterable!R);
enum isForeachUnaryIterable(R) =
is(typeof((R r) {
foreach (ref a; r)
cast(void)unaryFun!pred(a);
}));
enum isForeachBinaryIterable(R) =
is(typeof((R r) {
foreach (i, ref a; r)
cast(void)binaryFun!BinaryArgs(i, a);
}));
enum isForeachIterable(R) =
(!isForwardRange!R || isDynamicArray!R) &&
(isForeachUnaryIterable!R || isForeachBinaryIterable!R);
void each(Range)(Range r)
if (isRangeIterable!Range && !isForeachIterable!Range)
{
debug(each) pragma(msg, "Using while for ", Range.stringof);
static if (isRangeUnaryIterable!Range)
{
while (!r.empty)
{
cast(void)unaryFun!pred(r.front);
r.popFront();
}
}
else // if (isRangeBinaryIterable!Range)
{
size_t i = 0;
while (!r.empty)
{
cast(void)binaryFun!BinaryArgs(i, r.front);
r.popFront();
i++;
}
}
}
void each(Iterable)(Iterable r)
if (isForeachIterable!Iterable)
{
debug(each) pragma(msg, "Using foreach for ", Iterable.stringof);
static if (isForeachUnaryIterable!Iterable)
{
foreach (ref e; r)
cast(void)unaryFun!pred(e);
}
else // if (isForeachBinaryIterable!Iterable)
{
foreach (i, ref e; r)
cast(void)binaryFun!BinaryArgs(i, e);
}
}
}
unittest
{
import std.range : iota;
long[] arr;
// Note: each over arrays should resolve to the
// foreach variant, but as this is a performance
// improvement it is not unit-testable.
iota(5).each!(n => arr ~= n);
assert(arr == [0, 1, 2, 3, 4]);
// in-place mutation
arr.each!((ref n) => n++);
assert(arr == [1, 2, 3, 4, 5]);
// by-ref lambdas should not be allowed for non-ref ranges
static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++))));
// default predicate (walk / consume)
auto m = arr.map!(n => n);
(&m).each();
assert(m.empty);
// in-place mutation with index
arr[] = 0;
arr.each!"a=i"();
assert(arr == [0, 1, 2, 3, 4]);
// opApply iterators
static assert(is(typeof({
import std.parallelism;
arr.parallel.each!"a++";
})));
}
/**
$(D auto filter(Range)(Range rs) if (isInputRange!(Unqual!Range));)

View file

@ -375,147 +375,6 @@ package template algoFormat()
alias algoFormat = format;
}
// each
/**
Eagerly iterates over $(D r) and calls $(D pred) over _each element.
Params:
pred = predicate to apply to each element of the range
r = range or iterable over which each iterates
Example:
---
void deleteOldBackups()
{
import std.algorithm, std.datetime, std.file;
auto cutoff = Clock.currTime() - 7.days;
dirEntries("", "*~", SpanMode.depth)
.filter!(de => de.timeLastModified < cutoff)
.each!remove();
}
---
If the range supports it, the value can be mutated in place. Examples:
---
arr.each!((ref a) => a++);
arr.each!"a++";
---
If no predicate is specified, $(D each) will default to doing nothing
but consuming the entire range. $(D .front) will be evaluated, but this
can be avoided by explicitly specifying a predicate lambda with a
$(D lazy) parameter.
$(D each) also supports $(D opApply)-based iterators, so it will work
with e.g. $(XREF parallelism, parallel).
See_Also: $(XREF range,tee)
*/
template each(alias pred = "a")
{
alias BinaryArgs = TypeTuple!(pred, "i", "a");
enum isRangeUnaryIterable(R) =
is(typeof(unaryFun!pred(R.init.front)));
enum isRangeBinaryIterable(R) =
is(typeof(binaryFun!BinaryArgs(0, R.init.front)));
enum isRangeIterable(R) =
isInputRange!R &&
(isRangeUnaryIterable!R || isRangeBinaryIterable!R);
enum isForeachUnaryIterable(R) =
is(typeof((R r) {
foreach (ref a; r)
cast(void)unaryFun!pred(a);
}));
enum isForeachBinaryIterable(R) =
is(typeof((R r) {
foreach (i, ref a; r)
cast(void)binaryFun!BinaryArgs(i, a);
}));
enum isForeachIterable(R) =
(!isForwardRange!R || isDynamicArray!R) &&
(isForeachUnaryIterable!R || isForeachBinaryIterable!R);
void each(Range)(Range r)
if (isRangeIterable!Range && !isForeachIterable!Range)
{
debug(each) pragma(msg, "Using while for ", Range.stringof);
static if (isRangeUnaryIterable!Range)
{
while (!r.empty)
{
cast(void)unaryFun!pred(r.front);
r.popFront();
}
}
else // if (isRangeBinaryIterable!Range)
{
size_t i = 0;
while (!r.empty)
{
cast(void)binaryFun!BinaryArgs(i, r.front);
r.popFront();
i++;
}
}
}
void each(Iterable)(Iterable r)
if (isForeachIterable!Iterable)
{
debug(each) pragma(msg, "Using foreach for ", Iterable.stringof);
static if (isForeachUnaryIterable!Iterable)
{
foreach (ref e; r)
cast(void)unaryFun!pred(e);
}
else // if (isForeachBinaryIterable!Iterable)
{
foreach (i, ref e; r)
cast(void)binaryFun!BinaryArgs(i, e);
}
}
}
unittest
{
long[] arr;
// Note: each over arrays should resolve to the
// foreach variant, but as this is a performance
// improvement it is not unit-testable.
iota(5).each!(n => arr ~= n);
assert(arr == [0, 1, 2, 3, 4]);
// in-place mutation
arr.each!((ref n) => n++);
assert(arr == [1, 2, 3, 4, 5]);
// by-ref lambdas should not be allowed for non-ref ranges
static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++))));
// default predicate (walk / consume)
auto m = arr.map!(n => n);
(&m).each();
assert(m.empty);
// in-place mutation with index
arr[] = 0;
arr.each!"a=i"();
assert(arr == [0, 1, 2, 3, 4]);
// opApply iterators
static assert(is(typeof({
import std.parallelism;
arr.parallel.each!"a++";
})));
}
/**
Forwards function arguments with saving ref-ness.
*/