TransWikia.com

Генераторы в C++

Stack Overflow на русском Asked by dIm0n on February 6, 2021

Как в C++ написать генератор, как в питоне?

Например, для чисел Фибоначчи генератор можно написать и использовать так:

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

g = fib()
print(next(g), next(g), next(g))

for f in fib():
  print(f)
  if (f > 1000):
    break

Как сделать то же в C++?

4 Answers

Ну, раз пошла такая пьянка...

Я бы делал в стиле итератора:

class Fib
{
public:
    Fib():f0(0),f1(1){}
    Fib& operator ++(int) { unsigned long long f = f0+f1; f0 = f1; f1 = f; return *this; }
    unsigned long long operator*() { return f0; }

private:
    unsigned long long f0, f1;
};

int main(int argc, const char * argv[])
{
    for(Fib f; *f < 1000000000000ull;)
        cout << *f++ << endl;
}

Answered by Harry on February 6, 2021

Пускай лучше считает компилятор:

//некоторое число Фибоначчи
template<size_t n>
struct Fib {
    enum { res = Fib<n - 1>::res + Fib<n - 2>::res };
};
template<>
struct Fib<0> {
    enum { res = 0 };
};
template<>
struct Fib<1> {
    enum { res = 1 };
};

//шаблон, хранивший массив

template <size_t n, size_t...args>
struct Fib_аrr {
    static constexpr auto& array =
        Fib_arr<n - 1, Fib<n>::res, args...>::array;
};
template <size_t...args>
struct Fib_arr<0, args...> {
    static constexpr int array[] = { 0, args... };
};

//теперь массив чисел Фибоначчи до элемента,
// номер которого задан в параметре шаблона
template <size_t n>
struct Fib_array {
    static constexpr auto& array =
        Fib_arr<n>::array;
};
//а генератор выдаст случайный  элемент из массива
template <size_t s >
struct Fib_gen {
    int  operator ()() { return Fib_array<s>::array[(rand() % s)]; }
};

Программа просто выводит элемент массива

int main() {    
    //генерировать 20 случайных от 1 до 13 элемента  чисел Фибоначчи 
    Fib_gen<13> g;
    for (int i = 1; i < 20; ++i)
        cout << g() << ' '; 

   //или просто вывести 0... 20 элементы       
   for (int i = 0; i < 20; ++i)
        cout << Fib_array<20>::array[i] << ' ';
    return 0;
}

Answered by AR Hovsepyan on February 6, 2021

Почти так же:

#include <coroutine>
#include <tuple>
#include <iostream>

generator fib() {
    int a = 0, b = 1;

    while (true) {
        co_yield a;
        std::tie(a, b) = std::make_tuple(b, a + b);
    }
}

int main() {
    auto gen_1 = fib();

    std::cout << gen_1.current_value(); gen_1.move_next();
    std::cout << gen_1.current_value(); gen_1.move_next();
    std::cout << gen_1.current_value() << 'n';

    auto gen = fib();

    for (auto cur = gen.current_value();; gen.move_next(), cur = gen.current_value()) {
        std::cout << cur << 'n';
        if (cur > 1000)
            break;
    }
}

Чтобы было совсем похоже, надо ещё функцию next сделать, чтобы продвигала + возвращала значение генератора, и добавить итераторы для генератора, чтобы использовать в range for цикле вместе с break.

Компилить с -std=c++2a -fcoroutines.

Если в стандартной библиотеке ещё не реализован генератор, то вот:

#include <coroutine>
#include <exception>

struct generator {
    struct promise_type {
        int current_value;
        static auto get_return_object_on_allocation_failure() { return generator{nullptr}; }
        auto get_return_object() { return generator{handle::from_promise(*this)}; }
        auto initial_suspend() { return std::suspend_always{}; }
        auto final_suspend() { return std::suspend_always{}; }
        void unhandled_exception() { std::terminate(); }
        void return_void() {}
        auto yield_value(int value) {
            current_value = value;
            return std::suspend_always{};
        }
    };

    using handle = std::coroutine_handle<promise_type>;

    bool move_next() { return coro ? (coro.resume(), !coro.done()) : false; }
    int current_value() { return coro.promise().current_value; }
    generator(generator const&) = delete;
    generator(generator&& rhs) : coro(rhs.coro) { rhs.coro = nullptr; }
    ~generator() { if (coro) coro.destroy(); }
private:
    generator(handle h) : coro(h) {}
    handle coro;
};

Тест https://godbolt.org/z/KK41sv

Answered by dIm0n on February 6, 2021

думаю можно через класс, просто хранить состояние в обьекте, и каждый раз получать следующее значение через next

#include <iostream>

class Fibonacci
{
private:
  int a, b, c;
public:
  Fibonacci() :a(0), b(1), c(0) {}
  friend int next(Fibonacci& fib)
  {
    fib.c = fib.a + fib.b;
    fib.a = fib.b;
    fib.b = fib.c;
    return fib.c;
  }
};


int main() 
{
  Fibonacci fib;

  for (int i = 0;i < 15;++i) {
    std::cout << next(fib) << 'n';
  }

  return 0;
}

еще можно добавить итераторы чтобы можно было итерироваться в range-based for

Answered by Ildar on February 6, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP