Доступ к предыдущему элементу внутри функции for_each - PullRequest
0 голосов
/ 08 января 2020

Я пытаюсь реализовать последовательность Фибоначчи в C ++ 17, используя for_each

#include <list>
#include <algorithm>
using namespace std;
int main(int argc, char* argv[]) { 
  list<int> mylist = {1,2,3,4,5};
  for_each( next(mylist.begin()), mylist.end(), [](int &n){n = n + prev(n);});
}

для доступа к предыдущему элементу внутри функции, prev не работает. Как я могу получить доступ к предыдущему элементу внутри функции? Спасибо.

Ответы [ 2 ]

3 голосов
/ 08 января 2020

Во-первых, гораздо проще использовать std::adjacent_difference для операций с последовательными элементами.

Если вы действительно хотите улучшить понимание std::for_each, тогда это возможно, но вам придется взять на себя ответственность за обновите теневой итератор самостоятельно, так как невозможно перейти от элемента к итератору:

#include <algorithm>
#include <iostream>
#include <list>

int main()
{
    std::list<int> v(15);
    v.front() = 1;

    auto it = v.begin();
    auto prev = it;
    auto current = *it++;
    std::for_each(it, v.end(),
             [&current,&prev](int& n){ std::swap(n, current); current = n+*prev++;});

    for (auto n: v) {
        std::cout << n << '\n';
    }
}

Однако, если мы используем std::vector для нашего хранилища, тогда у нас есть способ получить доступ к предыдущий элемент, поскольку векторы хранят свои элементы непрерывно:

#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v(15);
    v[0] = v[1] = 1;

    std::for_each(v.begin()+2, v.end(),
             [](int& n){ n = (&n)[-1] + (&n)[-2];});

    for (auto n: v) {
        std::cout << n << '\n';
    }
}
0 голосов
/ 08 января 2020

Даже если бы вы могли использовать prev для доступа к предыдущему элементу, неясно, как опубликованный алгоритм будет генерировать ожидаемый результат (последовательность Фибоначчи), потому что переданный список инициализируется как

list<int> mylist = {1, 2, 3, 4, 5};

Лямбда добавила (если бы это работало) значение предыдущего элемента (уже измененного!) К current one, что привело бы к последовательности: 1, 2 + 1 = 3 , 3 + 3 = 6, ...

Может быть проще использовать лямбду с сохранением состояния и другой алгоритм, например std::generate (или std::generate_n).

#include <algorithm>
#include <array>
#include <iostream>
#include <iterator>
#include <list>
#include <utility>

template <class T>
auto make_fibonacci_generator(T first, T second)
{
    return [prev = first, cur = second] () mutable {
        return cur += std::exchange(prev, cur);
    };
}

template <class OutIt, class T = typename OutIt::value_type>
auto gen_n_fibonacci_numbers(T first, T second, std::size_t n, OutIt dest)
{
    if ( n == std::size_t(0) )
        return dest;
    *dest++ = first;
    if ( n == std::size_t(1) )
        return dest;
    *dest++ = second;
    if ( n == std::size_t(2) )
        return dest;
    return std::generate_n(dest, n - 2, make_fibonacci_generator(first, second));
}


template <class OutIt, class T = typename OutIt::value_type>
void gen_fibonacci_numbers(T first, T second, OutIt dest, OutIt dest_last)
{
    if ( dest == dest_last )
        return;
    *dest++ = first;
    if ( dest == dest_last )
        return;
    *dest++ = second;
    if ( dest == dest_last )
        return;
    std::generate(dest, dest_last, make_fibonacci_generator(first, second));
}


int main()
{
    std::list<unsigned int> numbers;
    gen_n_fibonacci_numbers(0, 1, 20, std::back_inserter(numbers));


    for (auto i : numbers)
        std::cout << ' ' << i;
    std::cout << '\n';

    std::array<int, 10> nums;
    gen_fibonacci_numbers(0, 1, nums.begin(), nums.end());

    for (auto i : nums)
        std::cout << ' ' << i;
    std::cout << '\n';
}

Тестируемый здесь .

...