Skip to content
Merged
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
1 change: 1 addition & 0 deletions changelog.dd
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ for received content. Which generally makes all calls slightly faster. Up to 200
amounts of data.)
$(LI $(XREF std_meta, Repeat) was added to obtain a repeating
$(XREF std_meta, AliasSeq) consisting of template arguments.)
$(LI $(D fold) was added as an alternative to $(D reduce) with argument order swapped.)

)

Expand Down
78 changes: 78 additions & 0 deletions std/algorithm/iteration.d
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ $(T2 filter,
$(T2 filterBidirectional,
Similar to $(D filter), but also provides $(D back) and $(D popBack) at
a small increase in cost.)
$(T2 fold,
$(D fold!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).)
$(T2 group,
$(D group([5, 2, 2, 3, 3])) returns a range containing the tuples
$(D tuple(5, 1)), $(D tuple(2, 2)), and $(D tuple(3, 2)).)
Expand Down Expand Up @@ -2520,6 +2522,8 @@ template reduce(fun...) if (fun.length >= 1)
$(D reduce) will operate on an unqualified copy. If this happens
then the returned type will not perfectly match $(D S).

Use $(D fold) instead of $(D reduce) to use the seed version in a UFCS chain.

Params:
fun = one or more functions
seed = the initial value of the accumulator
Expand Down Expand Up @@ -2868,6 +2872,80 @@ private template ReduceSeedType(E)
}
}


/++
Implements the homonym function (also known as $(D accumulate), $(D
Copy link
Member

Choose a reason for hiding this comment

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

Would be nice to use backticks instead of $(D ...) throughout (not a blocker).

compress), $(D inject), or $(D foldl)) present in various programming
languages of functional flavor. The call $(D fold!(fun)(range, seed))
first assigns $(D seed) to an internal variable $(D result),
also called the accumulator. Then, for each element $(D x) in $(D
range), $(D result = fun(result, x)) gets evaluated. Finally, $(D
result) is returned. The one-argument version $(D fold!(fun)(range))
works similarly, but it uses the first element of the range as the
seed (the range must be non-empty).

Returns:
the accumulated $(D result)

See_Also:
$(WEB en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))

$(LREF sum) is similar to $(D fold!((a, b) => a + b)) that offers
precise summing of floating point numbers.

This is functionally equivalent to $(LREF reduce) with the argument order reversed,
and without the need to use $(LREF tuple) for multiple seeds.
+/
template fold(fun...) if (fun.length >= 1)
{
auto fold(R, S...)(R r, S seed)
{
static if (S.length < 2)
{
return reduce!fun(seed, r);
}
else
{
import std.typecons: tuple;
return reduce!fun(tuple(seed), r);
}
}
}

///
@safe pure unittest
{
immutable arr = [1, 2, 3, 4, 5];

// Sum all elements
assert(arr.fold!((a, b) => a + b) == 15);

// Sum all elements with explicit seed
assert(arr.fold!((a, b) => a + b)(6) == 21);

import std.algorithm.comparison: min, max;
import std.typecons: tuple;

// Compute minimum and maximum at the same time
assert(arr.fold!(min, max) == tuple(1, 5));

// Compute minimum and maximum at the same time with seeds
assert(arr.fold!(min, max)(0, 7) == tuple(0, 7));
Copy link
Member

Choose a reason for hiding this comment

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

What about adding an example of a UFCS chain, which is the primary motivation for introducing this function over reduce in the first place?


// Can be used in a UFCS chain
assert(arr.map!(a => a + 1).fold!((a, b) => a + b) == 20);
}

@safe @nogc pure nothrow unittest
{
int[1] arr;
static assert(!is(typeof(arr.fold!())));
static assert(!is(typeof(arr.fold!(a => a))));
static assert(is(typeof(arr.fold!((a, b) => a))));
static assert(is(typeof(arr.fold!((a, b) => a)(1))));
}


// splitter
/**
Lazily splits a range using an element as a separator. This can be used with
Expand Down
1 change: 1 addition & 0 deletions std/algorithm/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ $(TR $(TDNW Iteration)
$(SUBREF iteration, each)
$(SUBREF iteration, filter)
$(SUBREF iteration, filterBidirectional)
$(SUBREF iteration, fold)
$(SUBREF iteration, group)
$(SUBREF iteration, joiner)
$(SUBREF iteration, map)
Expand Down