std::iota的功能非常有限

23

作为来自Python世界的我,我发现函数std::iota非常有限。为什么该接口限制不能使用任何UnaryFunction

例如,我可以将

>>> x = range(0, 10)

进入

std::vector<int> x(10);
std::iota(std::begin(x), std::end(x), 0);

但是,如何去做:

>>> x = range(0,20,2)

甚至更多

>>> x = range(10,0,-1)

我知道编写这样的函数或使用Boost很简单,但我认为C++委员会必须仔细挑选了这个设计。因此,显然我从C++11中错过了某些东西。


1
如果你想在向量上执行其他操作,可以使用 std::transform - Jaa-c
2
请查看 https://dev59.com/RHI-5IYBdhLWcg3wMFS0。 - vaultah
3
看一下std::generate,但归根结底,在C++标准库中还没有一个真正优雅的解决方案。 - MikeMB
2
"iota"是一个简单的函数,适用于简单的需求。对于更复杂的需求,可以使用"generate"或"transform"。 - Nicol Bolas
我发现Python的range令人困惑,它根据参数数量改变其位置参数的含义。对我来说,等价功能在iotagenerate(+ lambda)之间分配是一个优点。 - Caleth
3个回答

37

那么 std::generate 怎么样?

int n = -2;
std::generate(x.begin(), x.end(), [&n]{ return n+=2; }); 
int n = 10;
std::generate(x.begin(), x.end(), [&n]{ return n--;});

2
然而,尽管这可能会解决问题,但我认为作者是在询问将std::iota重载为类似于std::iota(std::begin(x), std::end(x), 0, 2)的东西,其中2是迭代的步长——为什么C++11没有像这样的函数。因此,在我看来,这个问题更适合向C++标准委员会提出。 - Victor Polevoy

16

But how would one do:

x = range(0,20,2)

除了使用std::generate()(请参见其他答案)之外,您还可以向std::iota()提供自己的一元函数,只需将其命名为operator++()

#include <iostream>
#include <functional>
#include <numeric>
#include <vector>

template<class T>
struct IotaWrapper
{
    typedef T type;
    typedef std::function<type(const type&)> IncrFunction;

    type value;
    IncrFunction incrFunction;

    IotaWrapper() = delete;
    IotaWrapper(const type& n, const IncrFunction& incrFunction) : value(n), incrFunction(incrFunction) {};

    operator type() { return value; }
    IotaWrapper& operator++() { value = incrFunction(value); return *this; }
};

int main()
{
    IotaWrapper<int> n(0, [](const int& n){ return n+2; });
    std::vector<int> v(10);
    std::iota(v.begin(), v.end(), n);

    for (auto i : v)
        std::cout << i << ' ';
    std::cout << std::endl;
}

输出: 0 2 4 6 8 10 12 14 16 18

演示


以下是如何实现Range()的想法:

struct Range
{
    template<class Value, class Incr>
    std::vector<Value> operator()(const Value& first, const Value& last, const Incr& increment)
    {
        IotaWrapper<Value> iota(first, [=](const int& n){ return n+increment; });
        std::vector<Value> result((last - first) / increment);
        std::iota(result.begin(), result.end(), iota);
        return result;
    }
};

Demo


7

有了C++20的范围(Ranges)功能,你可以这样写:

static auto stepped_iota(int start, int step) {
  return std::ranges::views::iota(0) |
         std::ranges::views::transform([=](int x) { return x * step + start; });
}

void f() {
  for (int x : stepped_iota(0, 2)) { ... }
}

https://godbolt.org/z/3G49rs

或者,如果你希望范围是有限的:

static auto stepped_iota(int start, int end, int step) {
  return std::ranges::views::iota(0, (end - start + step - 1) / step) |
         std::ranges::views::transform([=](int x) { return x * step + start; });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接