如何实现能够返回生成器的模板驱动的嵌套 for 循环?

How can I implement a template-driven nested for-loop that is capable of returning a generator?

提问人:Danny 提问时间:11/5/2023 最后编辑:Danny 更新时间:11/7/2023 访问量:57

问:

我正在尝试实现一个在多维数组上提供下标的生成器。具体来说,我正在使用 CPP Chrono 的生成器实现。由于维数由输入数组的大小决定,因此我需要一个递归函数来生成嵌套循环。到目前为止,我的逻辑适用于一个大小扣除的嵌套 for 循环:shapefor

#include <iostream>
#include <array>

using Size_t = size_t;
using Int64_t = int;

/// @brief Nested for loop helper
/// @details Recursively generates NLoops nested for loops
template <size_t Index = 0, Size_t NLoops>
void nested_for_loop(const std::array<Size_t, NLoops>& shape, std::array<Int64_t, NLoops>& outSubscripts) {
    if constexpr (Index == NLoops) {
        // Base case: All nested loops have run.
        // Operation is performed here
        for (size_t i = 0; i < NLoops; ++i) {
            std::cout << outSubscripts[i] << ' ';
        }
        std::cout << std::endl;

    } else {
        // Recursive case: Loop from 0 to array[Index]
        for (size_t i = 0; i <= shape[Index]; ++i) {
            outSubscripts[Index] = i; // Store the current subscript
            nested_for_loop<Index + 1>(shape, outSubscripts); // Recursively call for the next loop
        }
    }
}

int main() {
    std::array<Size_t, 3> shape = {2, 30, 6};
    std::array<int, 3> outSubscripts{};

    Int64_t outIndex = 0;
    nested_for_loop(shape, outSubscripts);

    return 0;
}

这能够打印出我想要的下标。如果这只是一个常规的旧 for 循环,我可以在操作逻辑所在的位置添加一个,将函数返回类型更新为 ,然后调用它。但是,似乎不可能将这种递归方法转换为生成器函数。co_yieldgenerator<MyDesiredArrayType>

有没有更好的方法来展开这个循环,以便我可以正确地从协程中产生?我唯一能想到的方法就是这个答案中描述的方法。我宁愿不必求助于这样的东西,因为我认为没有一种方法可以将线性循环索引转换为所需的下标,而且我不想产生额外的加法/乘法的运行时成本。forconstexprN

我总是可以选择手动写出一系列语句。我可以添加条件块,所以我会支持从 0 到嵌套的 for 循环。然而,这将是如此乏味和限制,以至于它是愚蠢的。肯定有更好的方法!if constexprMM

C++ for 生成器 嵌套循环 协程

评论


答:

0赞 Jarod42 11/7/2023 #1

您可以将递归函数转换为简单的迭代函数:

template <std::size_t N>
bool next_cartesian_product(const std::array<std::size_t, N>& shape,
                            std::array<std::size_t, N>& cur)
{
    for (std::size_t i = 0; i != N; ++i) {
        if (++cur[N - 1 - i] > shape[N - 1 - i]) {
            cur[N - 1 - i] = 0;
        } else {
            return true;
        }
    }
    return false;
}

演示

现在把它变成发电机应该是微不足道的。