Алгоритм С++ для применения функции к последовательным элементам

Есть ли более простой способ написать это, например. используя алгоритм STL или boost?

std::vector<int> v { 0, 1, 2, 3 }; // any generic STL container
std::vector<int> result;
std::transform(v.begin(), v.end() - 1, // (0, 1, 2)
               v.begin() + 1,          // (1, 2, 3)
               std::back_inserter(result),
               [](int a, int b){ return a + b; }); // any binary function
// result == { 1, 3, 5 }

Ответ 2

Я предлагаю использовать цикл for:

for(std::vector::size_type i = 0; i < v.size() - 1; i++)
    result.push_back(v[i] + v[i+1])

Более общий цикл для двунаправленных итераторов:

// let begin and end be iterators to corresponding position
// let out be an output iterator
// let fun be a binary function
for (auto it = begin, end_it = std::prev(end); it != end_it; ++it)
   *out++ = fun(*it, *std::next(it));

Мы можем пойти немного дальше и написать цикл для итераторов вперед:

if(begin != end) {
    for (auto curr = begin,
         nxt = std::next(begin); nxt != end; ++curr, ++nxt) {
        *out++ = fun(*curr, *nxt);
    }
}

Наконец, и алгоритм ввода итераторов. Однако для этого требуется, чтобы тип значения был скопирован.

if(begin != end) {
    auto left = *begin;
    for (auto it = std::next(begin); it != end; ++it) {
        auto right = *it;
        *out++ = fun(left, right);
        left = right;
    }
}

Ответ 3

Я бы написал свой собственный алгоритм, чтобы применить функтор к каждой паре элементов в контейнере.

(Бесстыдный рекламный ролик) В моей презентации ACCU в этом году " STL Algorithms - Как использовать их и как написать свой собственный, показал как написать такое вот так. Я назвал его adjacent_pair (около 25:00 в видео)

template <typename ForwardIterator, typename Func>
void adjacent_pair(ForwardIterator first, ForwardIterator last, Func f)
{
    if (first != last)
    {
        ForwardIterator trailer = first;
        ++first;
        for (; first != last; ++first, ++trailer)
            f(*trailer, *first);
    }
}

Ответ 4

std::adjacent_difference предназначен именно для этого, но, как вы упомянули, он копирует первый элемент в результат, который вам не нужен. Используя Boost.Iterator, довольно легко сделать back_inserter, который отбрасывает первый элемент.

#include <boost/function_output_iterator.hpp>

template <class Container>
auto mybackinsrtr(Container& cont) {
    // Throw away the first element
    return boost::make_function_output_iterator(
            [&cont](auto i) -> void { 
              static bool first = true;
              if (first)
                first = false;
              else
                cont.push_back(i);
            });
}

Тогда вы можете #include <boost/range/numeric.hpp> и сделать это:

std::vector<int> v { 0, 1, 2, 3 }; // any generic STL container
std::vector<int> result;
boost::adjacent_difference(v, mybackinsrtr(result), std::plus<>{}); // any binary function

Посмотрите на идеон


Если вы хотите, чтобы ваша двоичная функция возвращала другой тип (например, строку), вышеупомянутое решение не будет работать, потому что, хотя вставка cont.push_back(i) никогда не вызывается для первого скопированного элемента, она все равно должна быть скомпилирован, и он не пойдет.

Итак, вы можете вместо этого сделать back_inserter, который игнорирует любые элементы другого типа, чем в контейнере. Это игнорирует первый, скопированный элемент и принимает остальные.

template <class Container>
struct ignore_insert {
    // Ignore any insertions that don't match container type
    Container& cont;
    ignore_insert(Container& c) : cont(c) {}
    void operator() (typename Container::value_type i) {
        cont.push_back(i);
    }
    template <typename T>
    void operator() (T) {}
};

template <class Container>
auto ignoreinsrtr(Container& cont) {
    return boost::make_function_output_iterator(ignore_insert<Container>{cont});
}

Затем вы можете использовать его аналогично.

std::vector<int> v { 0, 1, 2, 3 }; // any generic STL container
std::vector<std::string> result;
boost::adjacent_difference(v, ignoreinsrtr(result), [](int a, int b){ return std::to_string(a+b); });

На идее