C++ 中是否有与 Python range() 等效的函数?

RUS*_*ker 6 c++ c++20

我想用来std::for_each并行迭代范围内的向量索引,计算Weierstrass 函数[a, b)的值并将其写入:std::vector

std::vector<std::array<float, 2>> values(1000);
auto range = /** equivalent of Pyhthon range(0, values.size()) **/;

std::for_each(std::execution::par, range.begin(), range.end(), [&](auto &&i) {
    values[i][0] = static_cast<float>(i) / resolution;
    values[i][1] = weierstrass(a, b, static_cast<float>(i) / resolution);
});

// a, b, and resolution are some constants defined before
// weierstrass() is the Weierstrass function
Run Code Online (Sandbox Code Playgroud)

我在互联网上找到了一些解决方案,但所有这些解决方案都需要包含一些第三方库或创建我自己的范围类。有没有标准的解决方案?

Art*_*oul 14

您可以使用std::views::iota(),它的用法与 Python 的类似(但有点不同)range()在std::ranges::for_each()的帮助下。两者都可以在 C++20 中使用。

在线尝试一下!

#include <algorithm>
#include <ranges>
#include <iostream>

int main() {
    std::ranges::for_each(std::views::iota(1, 10), [](int i) {
        std::cout << i << ' ';
    });
}
Run Code Online (Sandbox Code Playgroud)

输出:

1 2 3 4 5 6 7 8 9 
Run Code Online (Sandbox Code Playgroud)

正如@Afshin 所指出的,上面提到的代码std::ranges::for_each()不支持std::execution::par多线程执行。

为了解决这个问题,您可以将 iota 与常规一起使用,std::for_each()如下所示:

在线尝试一下!

1 2 3 4 5 6 7 8 9 
Run Code Online (Sandbox Code Playgroud)

输出:

1 2 3 4 5 6 7 8 9 
Run Code Online (Sandbox Code Playgroud)

Range我决定根据它在 Python 中的工作方式从头开始实现类加迭代器range()

与 Python 类似,您可以通过三种方式使用它:Range(stop)Range(start, stop)Range(start, stop, step)。这三个都支持任何负值。

为了测试实现的正确性,我填充了两个无序集合,一个包含所有生成的值,另一个包含所有使用的线程 ID(以表明它实际上使用了多核 CPU 执行)。

虽然我将迭代器标记为随机访问类型,但它仍然缺少一些方法,如-=--运算符,这些额外的方法是为了进一步改进。但对于std::for_each()它的使用有足够的方法。

如果我在实施中犯了一些错误,请在我的答案中添加评论并进行解释。

在线尝试一下!

#include <algorithm>
#include <ranges>
#include <iostream>
#include <execution>

int main() {
    auto range = std::views::iota(1, 10);
    std::for_each(std::execution::par, range.begin(), range.end(),
        [](int i) {
            std::cout << i << ' ';
        });
}
Run Code Online (Sandbox Code Playgroud)

输出:

Threads:
1628
9628
5408
2136
2168
8636
2880
6492
1100
Correct values: true
Run Code Online (Sandbox Code Playgroud)

  • 最好提及它是 C++20。:) (2认同)

kor*_*zck 5

如果问题在于创建类似于 python 的范围,range()您可以查看https://en.cppreference.com/w/cpp/iterator/iterator并使用它的示例:

#include <iostream>
#include <algorithm>
 
template<long FROM, long TO>
class Range {
public:
    // member typedefs provided through inheriting from std::iterator
    class iterator: public std::iterator<
                        std::input_iterator_tag,   // iterator_category
                        long,                      // value_type
                        long,                      // difference_type
                        const long*,               // pointer
                        long                       // reference
                                      >{
        long num = FROM;
    public:
        explicit iterator(long _num = 0) : num(_num) {}
        iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
        iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
        bool operator==(iterator other) const {return num == other.num;}
        bool operator!=(iterator other) const {return !(*this == other);}
        reference operator*() const {return num;}
    };
    iterator begin() {return iterator(FROM);}
    iterator end() {return iterator(TO >= FROM? TO+1 : TO-1);}
};
 
int main() {
    // std::find requires an input iterator
    auto range = Range<15, 25>();
    auto itr = std::find(range.begin(), range.end(), 18);
    std::cout << *itr << '\n'; // 18
 
    // Range::iterator also satisfies range-based for requirements
    for(long l : Range<3, 5>()) {
        std::cout << l << ' '; // 3 4 5
    }
    std::cout << '\n';
}
Run Code Online (Sandbox Code Playgroud)

  • 尽管我建议自己编写迭代器要求,而不是从 `std::iterator` 继承,因为它从 C++17 开始已被弃用。 (7认同)