简体   繁体   中英

Is there a fold like algorithm in std (or failing that: boost) available?

A very simpel example is multiplication - suppose I have a vector:

std::vector<int> ints = {1,2,3,4};

With a naive approach I can just use std::accumulate (or std::reduce ) and it looks like this:

int result = std::accumulate(ints.begin(), ints.end(), int{}, [](const int &a, const int &b){return a*b;});

but since the initial value is zero - the result becomes zero as well (For this specific case, one way I could fix it is by putting a '1' as initial).

I would rather use an algorithm that does the above but without an initial value 'side-effect' (ie. just multiply the numbers in vector).

A similar problem is often encountered within string handling where a delimiter must be inserted between elements.

What you're talking about can be reframed as a generalisation of accumulate over the last N-1 elements of your range, with the 1st element being the initial value.

So you can just write:

std::accumulate(std::next(std::begin(ints)), std::end(ints), *std::begin(ints), OP);

You have to assume that ints is non-empty, though, which raises my main point: what should a hypothetical standard function return when the range is empty? Should its results simply be undefined? Is that sensible?

Accumulate sidesteps this issue and provides a boatload of flexibility, by doing things the way it does. I think that's a good thing.

Combined with the ability to simply provide an appropriate initial value like 1 for your operation over the whole range, I'm not convinced there's much need for this hypothetical alternative in the standard.

It might also be difficult to come up with two names for it that mirror the already-asymmetrically-named "accumulate" and "reduce".


template <class InputIt, class T, class BinaryOperation>
T fold_if_you_really_want_to(InputIt first, InputIt last, BinaryOperation op)
{
    // UB if range is empty. Whatevs.
    T init = *first;
    return std::accumulate(++first, last, std::move(init), std::move(op));
}

…or something like that anyway. Note that this necessarily copies the first element; you could avoid that if you weren't lazy by calling into std::accumulate like I did. 😊

In addition to @Lightness Races in Orbit's answer, consider the case in Haskell: For cases like you described it (most prominently searching the maximum element in a list), Haskell delivers the functions foldl1 and foldr1 , which perform the fold over the collection and implicitly taking the first value as initial value. Yes, for the empty list this makes no sense, hence for this problem you have to provide a list with at least one element.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM