2016-05-19 2 views
15

Есть ли более простой способ написать это, например. используя алгоритм STL или boost?Алгоритм C++ для применения функции к последовательным элементам

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 } 
+1

@erip Итераторы и обратные итераторы несовместимы. 'v.end() - 1' и' v.rbegin() 'указывают на один и тот же элемент, но не могут сравниться. – interjay

+0

@interjay Упс. :) – erip

+1

Кажется, здесь было обсуждено без ответа: http://stackoverflow.com/questions/19927563/for-each-that-gives-two-or-n-adjacent-elements Кроме того, я конечно, я видел сообщение в блоге с реализацией, я отправлю его в качестве ответа, если найду его ... –

ответ

9

binary version of std::transform может использоваться.

Алгоритмы std::adjacent_find/ можно оскорблять.

+1

Разница с моим алгоритмом заключается в том, что 'смежный_difference' копирует первый элемент в последовательность вывода немодифицирована. Это на самом деле довольно глупо, потому что она вводит необходимость иметь совместимые типы ввода и вывода.:/ –

10

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

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; 
    } 
} 
+2

@FelixDombek Я добавил цикл для итераторов без случайного доступа (не так красиво). – user2079303

+0

'v.end() - 1' также потребует произвольный доступ, поэтому вместо этого вы должны использовать' std :: prev'. – interjay

+0

@interjay, вы правы. Хотя, это все равно не будет работать с итераторами вперед :( – user2079303

1

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

(Бесстыдный рекламный ролик) В моем выступлении в ACCU в этом году, «STL Algorithms – How to Use Them and How to Write Your Own», показано, как написать такой вариант. Я назвал его 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); 
    } 
} 
+1

Ну, если вы действительно хотите написать свой собственный алгоритм, я не остановлю вас: o) –

1

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 

See it on ideone


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

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

template <class Container> 
struct ignore_insert { 
    // Ignore any insertions that don't match container's 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); }); 

On ideone

+0

Приятно, но все же не работает для функтора, который изменил бы тип итератора ('[] (int a, int b) {std :: to_string (a + b);} '), однако эта функция может быть просто помещена в выходной итератор, устраняя необходимость в любом функторе, и вы можете просто использовать' std :: copy'. –

+0

@FelixDombek: Я не уверен, почему это не работает. Вы можете иметь дело с этим конкретным случаем [как я сделал здесь] (http://ideone.com/OOaDKP), заменив лямбда-вставку с помощью структуры, которая игнорирует 'int' и вставляет 'string'. – Kundor

+0

Он не работает с 'neighbour_Difference', потому что он копирует первый элемент в диапазон результатов вместо применения функтора. Таким образом, поскольку итератор результатов должен иметь возможность принимать элемент типа источника - у функтора тоже нет никакого несовместимого типа возврата. –

Смежные вопросы