LINUX.ORG.RU

c++ yild?

 , ,


0

3

А можно-ли в c++ реализовать аналог pythonовского yild? Например, есть код, который обходит многомерный массив, хочется сделать что-то вроде:

def pass(nx,ny,nz, data):
	for i in range(nx):
		for j in range(ny):
			for k in range(nz):
				yield i,j,k,data[i,j,k]
Подозреваю, что это можно сделать с помощью хитрого итератора, но не соображу как его финалить. В какую сторону купать?

★★★★★

Последнее исправление: thunar (всего исправлений: 1)
Ответ на: комментарий от thunar

конкретно yield-то тут причём тогда

anonymous
()
Ответ на: комментарий от paramon

range-v3 теперь ответ на все вопросы по С++. «Как мне почесать ухо через жопу?» - range-v3.

rupert ★★★★★
()
Ответ на: комментарий от Kroz

Вобщем, я так понял, что это всё еще в экспериментальном состоянии. GCC экспериментально поддерживает в версии 10 (https://gcc.gnu.org/projects/cxx-status.html), clang с версии 9.0. Но я на clang не смог запустить, а десятку GCC вот сечас ставлю.

И там основная хитрость - написать генератор - который по идее должен быть в стандартной библиотеке, но пока это не так.

Пару полезных ссылок:
- как запускать: https://stackoverflow.com/questions/62564094/c20-coroutines-need-a-function-t...
- код 1: https://godbolt.org/z/Wxhtty
- код 2: https://godbolt.org/z/jTS9BR , https://www.modernescpp.com/index.php/c-20-an-infinite-data-stream-with-corou...

Для Ъ:

// infiniteDataStream.cpp

#include <coroutine>
#include <memory>
#include <iostream>

template<typename T>
struct Generator {
    
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;
    
    Generator(handle_type h): coro(h) {}                         // (3)
    handle_type coro;
    std::shared_ptr<T> value;
    
    ~Generator() {
        if ( coro ) coro.destroy();
    }
    Generator(const Generator&) = delete;
    Generator& operator = (const Generator&) = delete;
    Generator(Generator&& oth): coro(oth.coro) {
        oth.coro = nullptr;
    }
    Generator& operator = (Generator&& oth) {
        coro = oth.coro;
        oth.coro = nullptr;
        return *this;
    }
    int getValue() {
        return coro.promise().current_value;
    }
    bool next() {                                                // (5)
        coro.resume();
        return not coro.done();
    }
    struct promise_type {
        promise_type()  = default;                               // (1)
          
        ~promise_type() = default;
        
        auto initial_suspend() {                                 // (4)
            return std::suspend_always{};
        }
        auto final_suspend() {
            return std::suspend_always{};
        }
        auto get_return_object() {                               // (2)
            return Generator{handle_type::from_promise(*this)};
        }
        auto return_void() {
            return std::suspend_never{};
        }
      
        auto yield_value(int value) {                            // (6) 
            current_value = value;
            return std::suspend_always{};
        }
        void unhandled_exception() {
            std::exit(1);
        }
        T current_value;
    };

};

Generator<int> getNext(int start = 0, int step = 1){
    auto value = start;
    for (int i = 0;; ++i){
        co_yield value;
        value += step;
    }
}

int main() {
    
    std::cout << std::endl;
  
    std::cout << "getNext():";
    auto gen = getNext();
    for (int i = 0; i <= 10; ++i) {
        gen.next();
        std::cout << " " << gen.getValue();                      // (7)
    }
    
    std::cout << "\n\n";
    
    std::cout << "getNext(100, -10):";
    auto gen2 = getNext(100, -10);
    for (int i = 0; i <= 20; ++i) {
        gen2.next();
        std::cout << " " << gen2.getValue();
    }
    
    std::cout << std::endl;
    
}


Output:

getNext(): 0 1 2 3 4 5 6 7 8 9 10

getNext(100, -10): 100 90 80 70 60 50 40 30 20 10 0 -10 -20 -30 -40 -50 -60 -70 -80 -90 -100

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
Ответ на: комментарий от thunar

Хм, не совсем то что нужно. Скорее это должно свестись к последовательному обходу, массива такой семантикой:

//data[nx][ny][nz];
for(auto&&[i,j,k,var] : pass<3>(data)){
  //var = data[i][j][k];
  //do stuff
}
Хорошо бы задать pass<> в виде рекурсивного шаблона в зависимости от числа измерений, т.к. нужно ещё насчитывать походу счёта некоторые коэффициенты по каждой из осей.

thunar ★★★★★
() автор топика
Последнее исправление: thunar (всего исправлений: 3)
Ответ на: комментарий от thunar

У тебя какие-то проблема с методичкой. Зачем в обходе счётчики дерьма? В том и смысл обхода, что в нём никаких счётчиков дерьма нет.

Ты хочешь сэкономить и не писать govno = data[i][j][k] - зачем?

tosol4152
()
Ответ на: комментарий от tosol4152

Сейчас сформулирую полную задачу.

Есть, например, в классе такой метод:

template<int n, int d>
struct foo_t{
	//n -- число измерений
	//d -- размер аргумента form_t 
	//data[nx][ny][nz]
	/* ... */
	void inline operator ()(const form_t<d> cfs[]){
		/* ... */
		if constexpr(n==3){
			for(auto&&[i,x] : cfs[0]){ //d-раз
			for(auto&&[j,y] : cfs[1]){ //d-раз
			for(auto&&[k,z] : cfs[2]){ //d-раз
				auto cff = x*y*z;
				auto var = data[i][j][k];
				//do stuff with var
			}}}
			return;
		}
		/* ... */
	}
};

Соответственно, этих «do stuff with var» может быть много, и мне нужно вместо множества реализаций под каждый n и разные задачи вынести весь обход из метода в итератор, так что бы семантически это было что-то вроде

constexpr int n = 3, d=4;
foo_t<n,d> foo;
form_t<d> forms[n];
for(auto&&[cff,var] : pass(foo, cfs){
	//do stuff
}
Т.к. n и d — константы компиляции, то очень хочется сделать это рекурсивным шаблоном.

thunar ★★★★★
() автор топика
Последнее исправление: thunar (всего исправлений: 3)
Ответ на: комментарий от tosol4152

Массив фиксированной длины и отступ.

template<int d>
struct form_t{
	int   id;
	float cf[d];

	struct iterator; //возвращает пару {int, float}
	iterator begin() const;
	iterator end()   const;
};
id — начало отсчёта по оси, соответственно из массива по каждой оси итерируемся от id до id+d.

thunar ★★★★★
() автор топика
Последнее исправление: thunar (всего исправлений: 3)
Ответ на: комментарий от thunar

А зачем тебе итератор? Если ты не хочешь использовать ranges - это слишком заморочно. Сделай просто pass(foo, cfs, [](auto && cff, auto && var) {}) - или тебе именно итераторы нужны?

tosol4152
()
Ответ на: комментарий от thunar

К тому же, непонятно по поводу этого: data[nx][ny][nz] - размерность задаётся в рантайме? В компилтайме задаётся только размерность для form?

int id; - это так же рантайм-значение?

tosol4152
()
Ответ на: комментарий от tosol4152

или тебе именно итераторы нужны?

Нет, мне главное сделать один отлаженный шаблон на все случаи. Пока пытаюсь разобраться какая семантика лучше будет.

pass(foo, cfs, [](auto && cff, auto && var) {})

Хм... Передать лямбду, хм. У меня из здача там в одном случае суммируются

out += var*cff
и передаются наружу в out, в другом наоборот идёт запись в
var += cff*аргумент
причём атомарно. Не соображу, можно ли такое в лямбды затолкать...

thunar ★★★★★
() автор топика
Последнее исправление: thunar (всего исправлений: 3)
Ответ на: комментарий от tosol4152

К тому же, непонятно по поводу этого ... размерность задаётся в рантайме?

Да, там отдельный класс для аллоцируемого многомерного массива, не привожу его т.к. реализация не важна, лавно что доступ подмассиву по оператору [].

int id; - это так же рантайм-значение?

Да, id и cf — рантайм константы. Компайл-тайм константы только размерность n и длинна d.

thunar ★★★★★
() автор топика
Ответ на: комментарий от thunar

лавно что доступ подмассиву по оператору [].

Ну многомерный массив мусор - зачем лишние сложность и тормоза? К тому же, что-бы рекурсивно свернуть скобочки - нужно знать реализацию. Хотя-бы примерную.

Ну как минимум - возвращает ли каждая скобочка один и тот же тип?

tosol4152
()
Ответ на: комментарий от tosol4152

Он внутри одномерный, просто обёртка над смещениями.

thunar ★★★★★
() автор топика
Ответ на: комментарий от thunar

Решил поколдовать. Слишком какая-то древняя, страшная маздайская мура - мне лень с нею разбираться. Поэтому закостылил что-то похожее.

Вот это:


	void inline operator ()(const form_t<d> cfs[]){
		/* ... */
		if constexpr(n==3){
			for(auto&&[i,x] : cfs[0]){ //d-раз
			for(auto&&[j,y] : cfs[1]){ //d-раз
			for(auto&&[k,z] : cfs[2]){ //d-раз
				auto cff = x*y*z;
				auto var = data[i][j][k];
				//do stuff with var
			}}}
			return;
		}
		/* ... */
	}

Не особо понимаю что и как оно должно работать, да и лень разбираться. В таких случаях нужно сразу писать тесты, что-бы пацаны не гадали - работает ли у них что-то или нет.

Вроде как можно превратить в это:


auto pass2(auto && data, auto forms, auto && on) {
  std::span cfs{forms};
  for(auto && [idx, cf]: cfs[0]) {
    auto && data0 = data[idx]; auto && cff0 = cf;
    for(auto && [idx, cf]: cfs[1]) {
      auto && data1 = data0[idx]; auto && cff1 = cff0 * cf;
      for(auto && [idx, cf]: cfs[2]) {
        auto && data2 = data1[idx]; auto && cff2 = cff1 * cf;
        on(cff2, data2);
      }
    }
  }
}

А далее это в свою очередь можно написать рекурсивно. https://godbolt.org/z/hUpQxF - как-то так. Идея должна быть ясна. Чтобы оно нормально анролило - нужно выкинуть циклы дерьма.

tosol4152
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.