mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 21:51:40 +03:00
Implement std.parallelism.fold with static if branches instead of with template specializations
This commit is contained in:
parent
673c4e959d
commit
665e4adbce
1 changed files with 131 additions and 115 deletions
|
@ -2812,26 +2812,59 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/** Implements the homonym function (also known as `accumulate`, `compress`,
|
||||
`inject`, or `foldl`) present in various programming languages of
|
||||
functional flavor.
|
||||
|
||||
This is functionally equivalent to $(LREF reduce) except the range
|
||||
parameter comes first and there is no need to use $(REF_ALTTEXT
|
||||
`tuple`,tuple,std,typecons) for multiple seeds.
|
||||
|
||||
Params:
|
||||
functions = One or more functions
|
||||
|
||||
Returns:
|
||||
The accumulated result as a single value for single function and as
|
||||
a tuple of values for multiple functions
|
||||
|
||||
See_Also:
|
||||
Similar to $(REF _fold, std,algorithm,iteration), `fold` is a wrapper around $(LREF reduce).
|
||||
*/
|
||||
///
|
||||
template fold(functions...)
|
||||
{
|
||||
/** Implements the homonym function (also known as `accumulate`, `compress`,
|
||||
`inject`, or `foldl`) present in various programming languages of
|
||||
functional flavor.
|
||||
|
||||
`fold` is functionally equivalent to $(LREF reduce) except the range
|
||||
parameter comes first and there is no need to use $(REF_ALTTEXT
|
||||
`tuple`,tuple,std,typecons) for multiple seeds.
|
||||
|
||||
There may be one or more callable entities (`functions` argument) to
|
||||
apply.
|
||||
|
||||
Params:
|
||||
|
||||
args = Just the range to _fold over; or the range and one seed
|
||||
per function; or the range, one seed per function, and
|
||||
the work unit size
|
||||
|
||||
Returns:
|
||||
The accumulated result as a single value for single function and
|
||||
as a tuple of values for multiple functions
|
||||
|
||||
See_Also:
|
||||
Similar to $(REF _fold, std,algorithm,iteration), `fold` is a wrapper around $(LREF reduce).
|
||||
|
||||
Example:
|
||||
---
|
||||
static int adder(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
static int multiplier(int a, int b)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
|
||||
// Just the range
|
||||
auto x = taskPool.fold!adder([1, 2, 3, 4]);
|
||||
assert(x == 10);
|
||||
|
||||
// The range and the seeds (0 and 1 below; also note multiple
|
||||
// functions in this example)
|
||||
auto y = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1);
|
||||
assert(y[0] == 10);
|
||||
assert(y[1] == 24);
|
||||
|
||||
// The range, the seed (0), and the work unit size (20)
|
||||
auto z = taskPool.fold!adder([1, 2, 3, 4], 0, 20);
|
||||
assert(z == 10);
|
||||
---
|
||||
*/
|
||||
auto fold(Args...)(Args args)
|
||||
{
|
||||
import std.string : format;
|
||||
|
@ -2841,105 +2874,85 @@ public:
|
|||
args.length == 1 + functions.length + 1, // range, seeds, and workUnitSize
|
||||
format("Invalid number of arguments (%s): Should be an input range, %s optional seed(s)," ~
|
||||
" and an optional work unit size", Args.length, functions.length));
|
||||
return fold(args[0], args[1..$]);
|
||||
}
|
||||
|
||||
/** This is the overload that uses implicit seeds. (See important notes
|
||||
on implicit seeds under $(LREF reduce).)
|
||||
|
||||
Params:
|
||||
range = The range of values to _fold over
|
||||
*/
|
||||
auto fold(R)(R range)
|
||||
{
|
||||
return reduce!functions(range);
|
||||
}
|
||||
|
||||
///
|
||||
version(StdUnittest)
|
||||
unittest
|
||||
{
|
||||
static int adder(int a, int b) { return a + b; }
|
||||
auto result = taskPool.fold!adder([1, 2, 3, 4]);
|
||||
assert(result == 10);
|
||||
}
|
||||
|
||||
/** This is the overload that uses explicit _seeds. (See important notes
|
||||
on explicit _seeds under $(LREF reduce).)
|
||||
|
||||
Params:
|
||||
range = The range of values to _fold over
|
||||
seeds = One seed per function
|
||||
*/
|
||||
auto fold(R, Seeds...)(R range, Seeds seeds)
|
||||
if (Seeds.length == functions.length)
|
||||
{
|
||||
static if (Seeds.length == 1)
|
||||
auto range()
|
||||
{
|
||||
return reduce!functions(seeds, range);
|
||||
return args[0];
|
||||
}
|
||||
|
||||
static if (Args.length == 1)
|
||||
{
|
||||
// Just the range
|
||||
return reduce!functions(range);
|
||||
}
|
||||
else
|
||||
{
|
||||
import std.typecons : tuple;
|
||||
return reduce!functions(tuple(seeds), range);
|
||||
static if (functions.length == 1)
|
||||
{
|
||||
auto seeds()
|
||||
{
|
||||
return args[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto seeds()
|
||||
{
|
||||
import std.typecons : tuple;
|
||||
return tuple(args[1 .. functions.length+1]);
|
||||
}
|
||||
}
|
||||
|
||||
static if (Args.length == 1 + functions.length)
|
||||
{
|
||||
// The range and the seeds
|
||||
return reduce!functions(seeds, range);
|
||||
}
|
||||
else static if (Args.length == 1 + functions.length + 1)
|
||||
{
|
||||
// The range, the seeds, and the work unit size
|
||||
static assert(isIntegral!(Args[$-1]), "Work unit size must be an integral type");
|
||||
return reduce!functions(seeds, range, args[$-1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
version(StdUnittest)
|
||||
unittest
|
||||
{
|
||||
static int adder (int a, int b) { return a + b; }
|
||||
static int multiplier(int a, int b) { return a * b; }
|
||||
|
||||
auto result = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1);
|
||||
assert(result[0] == 10);
|
||||
assert(result[1] == 24);
|
||||
}
|
||||
|
||||
/** This is the overload that uses explicit seeds and work unit size.
|
||||
(See important notes on explicit seeds and work unit size under
|
||||
$(LREF reduce).)
|
||||
|
||||
Params:
|
||||
range = The range of values to _fold over
|
||||
args = One seed per function (`args[0..$-1]`) and the work unit
|
||||
size (`args[$-1]`)
|
||||
*/
|
||||
auto fold(R, Args...)(R range, Args args)
|
||||
if (Args.length == functions.length + 1)
|
||||
{
|
||||
static assert(isIntegral!(Args[$-1]), "Work unit size must be an integral type");
|
||||
|
||||
static if (Args.length == 2)
|
||||
{
|
||||
return reduce!functions(args[0], range, args[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
import std.typecons : tuple;
|
||||
return reduce!functions(tuple(args[0..$-1]), range, args[$-1]);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
version(StdUnittest)
|
||||
unittest
|
||||
{
|
||||
static int adder (int a, int b) { return a + b; }
|
||||
static int multiplier(int a, int b) { return a * b; }
|
||||
|
||||
// Single function produces single result
|
||||
auto result = taskPool.fold!adder([1, 2, 3, 4], 0, 20);
|
||||
assert(result == 10);
|
||||
|
||||
// Multiple functions produce a tuple of results
|
||||
auto results = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1, 30);
|
||||
assert(results[0] == 10);
|
||||
assert(results[1] == 24);
|
||||
}
|
||||
}
|
||||
|
||||
// This test is not included in the documentation because even though these
|
||||
// examples are for the inner fold() template, with their current location,
|
||||
// they would appear under the outer one. (We can't move this inside the
|
||||
// outer fold() template because then dmd runs out of memory possibly due to
|
||||
// recursive template instantiation, which is surprisingly not caught.)
|
||||
version(StdUnittest)
|
||||
@system unittest
|
||||
{
|
||||
static int adder(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
static int multiplier(int a, int b)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
|
||||
// Just the range
|
||||
auto x = taskPool.fold!adder([1, 2, 3, 4]);
|
||||
assert(x == 10);
|
||||
|
||||
// The range and the seeds (0 and 1 below; also note multiple
|
||||
// functions in this example)
|
||||
auto y = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1);
|
||||
assert(y[0] == 10);
|
||||
assert(y[1] == 24);
|
||||
|
||||
// The range, the seed (0), and the work unit size (20)
|
||||
auto z = taskPool.fold!adder([1, 2, 3, 4], 0, 20);
|
||||
assert(z == 10);
|
||||
}
|
||||
/**
|
||||
Gets the index of the current thread relative to this $(D TaskPool). Any
|
||||
thread not in this pool will receive an index of 0. The worker threads in
|
||||
|
@ -3473,21 +3486,24 @@ public:
|
|||
}
|
||||
|
||||
version(StdUnittest)
|
||||
unittest
|
||||
@system unittest
|
||||
{
|
||||
import std.algorithm : sum;
|
||||
import std.algorithm.iteration : sum;
|
||||
import std.range : iota;
|
||||
import std.typecons : tuple;
|
||||
|
||||
static int adder(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
enum N = 100;
|
||||
auto r = iota(1, N + 1);
|
||||
const expected = r.sum();
|
||||
|
||||
static auto adder(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
assert(taskPool.fold!adder(r) == expected);
|
||||
assert(taskPool.fold!adder(r, 0) == expected);
|
||||
assert(taskPool.fold!adder(r, 0, 42) == expected);
|
||||
assert(taskPool.fold!(adder, adder)(r, 0, 0, 42) == tuple(expected, expected));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue