使用std :: fill填充数量不断增加的向量


82

我想填充一个vector<int>using std::fill,但此向量后应包含一个数字,而不是一个值。

我尝试通过将函数的第三个参数迭代一个来实现此目的,但这只会给我一个填充有1或2的向量(取决于++运算符的位置)。

例:

vector<int> ivec;
int i = 0;
std::fill(ivec.begin(), ivec.end(), i++); // elements are set to 1
std::fill(ivec.begin(), ivec.end(), ++i); // elements are set to 2

25
使用std::iota代替std::fill(假设您的编译器足够新以支持它)。
杰里·科芬

1
不幸的是,这似乎是新标准的一部分(我不能使用)。我看到BOOST库具有这样的功能,但是它不接受向量(boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/…),而是某些自定义类型。还有其他选择吗?
BlackMamba 2013年

2
user1612880,如果您不能使用C ++ 11 / Boost,请使用Liran的代码。这不是一个要求每个操作都必须在一个单独的一行也没有提供给C源代码文件:-)字符的世界性的短缺
paxdiablo

并不是因为短缺,而是为了性能。但是,如果没有残酷的破解手段无法实现这一目标,我将使用Liran提供的解决方案。
BlackMamba 2013年

@ user1612880您是否尝试过使用std::vector。boost版本是一个功能模板,第一个参数的“类型名称”指定一个概念。很难说,因为我只能找到一个非常形式化的规范,而没有简单的描述,但是我认为这std::vector符合该概念。
James Kanze

Answers:


125

最好这样使用std::iota

std::vector<int> v(100) ; // vector with 100 ints.
std::iota (std::begin(v), std::end(v), 0); // Fill with 0, 1, ..., 99.

就是说,如果您没有任何c++11支持(在我的工作中仍然是一个真正的问题),请std::generate像这样使用:

struct IncGenerator {
    int current_;
    IncGenerator (int start) : current_(start) {}
    int operator() () { return current_++; }
};

// ...

std::vector<int> v(100) ; // vector with 100 ints.
IncGenerator g (0);
std::generate( v.begin(), v.end(), g); // Fill with the result of calling g() repeatedly.

6
到底iota代表什么?(看起来好像有人输错了同样清晰的字符itoa。)
卢克·厄舍伍德

9
它不代表任何东西,在希腊语中等于字母i。它是APL中类似功能的名称,而数组语言引发了Stepanov STL中的许多想法。
BoBTFish

1
啊哈,谢谢!好吧,这是一个单词,而不是首字母缩写;我现在可能还记得这件事。CPP Reference谈到了“初始值的增量”(请注意略有相似之处),所以我脑子里有了缩写。(而且,谷歌搜索并不能立即看出希腊的联系。)也感谢历史参考。
卢克·厄舍伍德

47

您应使用std::iota算法(在中定义<numeric>):

  std::vector<int> ivec(100);
  std::iota(ivec.begin(), ivec.end(), 0); // ivec will become: [0..99]

因为std::fill只是将给定的固定值分配给给定范围内的元素[n1,n2)。并使用从初始值开始然后使用的顺序增加的值std::iota填充给定范围。您也可以使用替代方法。 [n1, n2)++valuestd::generate

不要忘记这std::iota是C ++ 11 STL算法。但是许多现代的编译器都支持它,例如GCC,Clang和VS2012:http : //msdn.microsoft.com/zh-cn/library/vstudio/jj651033.aspx

PS此函数以编程语言APL中的整数函数命名,表示希腊字母iota。我推测最初在APL中选择了这个奇数名称是因为它类似于a “integer”(尽管在数学上,iota广泛用于表示复数的虚部)。


1
您可能要添加std :: iota来自C ++ 11
hetepeperfan

@AlexanderKaraberov但是大多数地方没有使用最新版本的编译器,并且不能使用C ++ 11。
James Kanze

1
iota早在15年前就已经在STL中使用了,所以一些编译器一直支持它,早于C ++ 11
Jonathan Wakely 2013年

2
该向量的大小为0。您需要在容器中添加一个大小:std :: vector <int> ivec(100); std :: iota(ivec.begin(),ivec.end(),0);
AKludges

13

我的首选(甚至在C ++ 11中)将是 boost::counting_iterator

std::vector<int> ivec( boost::counting_iterator<int>( 0 ),
                       boost::counting_iterator<int>( n ) );

或如果向量已经构建:

std::copy( boost::counting_iterator<int>( 0 ),
           boost::counting_iterator<int>( ivec.size() ),
           ivec.begin() );

如果您无法使用Boost :(std::generate如其他答案中所建议),或者counting_iterator自己实施(如果需要在各个地方使用)。(使用Boost,您可以使用transform_iterator的acounting_iterator来创建各种有趣的序列。如果没有Boost,您可以通过生成器对象类型的形式手动完成很多操作,std::generate也可以将其插入手写计数迭代器。)


在第一个代码段,其构造std::vector被称为?必须是范围构造函数,但可以boost::counting_iterator隐式转换为InputIterator吗?
CinCout

8

我已经看到了std :: generate的答案,但是您也可以通过在lambda中使用静态变量来“改善”它,而不是在函数外部声明一个计数器或创建一个generator类:

std::vector<int> vec;
std::generate(vec.begin(), vec.end(), [] {
    static int i = 0;
    return i++;
});

我觉得它更简洁


5
staticIMO在这里有错误的语义。我将使用广义捕获,[i = 0]() mutable以便很明显该变量的作用域为lambda的特定实例,而不是其生成的类类型。很难想象这样一种情况,即实践上会有差异,这可能表明设计存在问题,但是无论如何,我认为使用成员变量的语义是优越的。另外,它使代码更简洁;现在,lambda的主体可以是单个语句。
underscore_d

是的,看起来更好; 我之前从未见过在lambda捕获中初始化过的变量:)
brainsandwich

6

如果您不想使用C ++ 11功能,可以使用std::generate

#include <algorithm>
#include <iostream>
#include <vector>

struct Generator {
    Generator() : m_value( 0 ) { }
    int operator()() { return m_value++; }
    int m_value;
};

int main()
{
    std::vector<int> ivec( 10 );

    std::generate( ivec.begin(), ivec.end(), Generator() );

    std::vector<int>::const_iterator it, end = ivec.end();
    for ( it = ivec.begin(); it != end; ++it ) {
        std::cout << *it << std::endl;
    }
}

该程序打印0到9。


3
@bitmask当然可以,如果您有lambda std::iota,不是吗?
BoBTFish 2013年

1
@bitmask但这需要C ++ 11 :)
juanchopanza

2
itend都在for循环之外定义的事实是相当不直观和精简的。有什么原因吗?
Christian Rau

1
@FrerichRaabe第二个原因听起来很合理(尽管如此愚蠢的VS bug可能是完全合理的,尽管在项目设置中它是可更改的),但我不明白第一个原因,这是怎么回事std::vector<int>::const_iterator it = vec.begin(), end = ivec.end()(既不重复键入也不反复致电)?行更长,好吧,它是C ++,无论如何您都不会遇到断行的情况(而老式的80字符显示效果很好的时代已经过去了)。但这是我的品味问题,无论如何,您早就得到了我的支持。
Christian Rau

2
@ChristianRau:说实话:在实践中,我使用我正在编辑的文件中代码所使用的任何样式,即,我认为一致性比到目前为止我们提到的优点或缺点更高。
Frerich Raabe 2013年

6

我们可以使用算法头文件中存在的generate函数。

代码段:

#include<bits/stdc++.h>
using namespace std;


int main()
{
    ios::sync_with_stdio(false);

    vector<int>v(10);

    int n=0;

    generate(v.begin(), v.end(), [&n] { return n++;});

    for(auto item : v)
    {
      cout<<item<<" ";
    }
    cout<<endl;

    return 0;
}

这是一个非常优雅的答案,在一般情况下可能是最简洁的。
猫头鹰

1
IMOn通过广义捕获[n = 0]() mutable使lambda成为成员是更好的,因此它不会污染周围的范围。
underscore_d

我正在用这个。对于高级情况可能不是有用的,但对c ++初学者来说足够好了。如果可以使用lambda是一个很好的解决方案。
Abinash Dash

4

std :: iota限于序列n,n + 1,n + 2,...

但是,如果要用通用序列f(0),f(1),f(2)等填充数组怎么办?通常,我们可以避免使用状态跟踪生成器。例如,

int a[7];
auto f = [](int x) { return x*x; };
transform(a, a+7, a, [a, f](int &x) {return f(&x - a);});

将产生正方形序列

0 1 4 9 16 25 36

但是,此技巧不适用于其他容器。

如果您坚持使用C ++ 98,则可能会执行以下可怕的操作:

int f(int &x) { int y = (int) (long) &x / sizeof(int); return y*y; }

然后

int a[7];
transform((int *) 0, ((int *) 0) + 7, a, f);

但是我不建议这样做。:)


1
OP无法使用C ++ 11(无lambas)
yizzlez 2014年

2

这也有效

j=0;
for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){
    *it = j++;
}

2
当然,手动使用迭代器是可行的,但是如果OP想要这样做,他们就不会询问stdlib算法,对吗?
underscore_d

2

在性能方面,您应该通过reserve()结合使用push_back()下面的示例中的函数来初始化向量:

const int numberOfElements = 10;

std::vector<int> data;
data.reserve(numberOfElements);

for(int i = 0; i < numberOfElements; i++)
    data.push_back(i);

所有的std::fillstd::generate等被操作在现有的矢量含量范围内,且因此所述载体必须充满一些数据早。甚至执行以下操作:std::vector<int> data(10);创建一个向量,并将所有元素都设置为其默认值(如果为,则为0 int)。

上面的代码避免了在用您真正想要的数据填充矢量内容之前初始化矢量内容。该解决方案的性能在大型数据集上非常明显。


2

还有另一种选择-不使用iota。可以使用For_each + lambda表达式:

vector<int> ivec(10); // the vector of size 10
int i = 0;            // incrementor
for_each(ivec.begin(), ivec.end(), [&](int& item) { ++i; item += i;});

它起作用的两个重要方面:

  1. Lambda捕获外部作用域[&],这意味着我可以在表达式内部使用
  2. 作为参考传递的项目,因此在向量内部是可变的

1

如果您真的想使用std::fill并且仅限于C ++ 98,则可以使用以下内容,

#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>

struct increasing {
    increasing(int start) : x(start) {}
    operator int () const { return x++; }
    mutable int x;
};

int main(int argc, char* argv[])
{
    using namespace std;

    vector<int> v(10);
    fill(v.begin(), v.end(), increasing(0));
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    return 0;
}

1

谈到提升:

auto ivec = boost::copy_range<std::vector<int>>(boost::irange(5, 10));

1

我知道这是一个老问题,但是我目前正在使用来处理这个问题。它需要c ++ 14。

#include "htl.hpp"

htl::Token _;

std::vector<int> vec = _[0, _, 100];
// or
for (auto const e: _[0, _, 100]) { ... }

// supports also custom steps
// _[0, _%3, 100] == 0, 4, 7, 10, ...

1
kes 那是很难读的代码,而且在编写时在全局范围内也是无效的,因为以标识符开头的标识符_保留给那里的实现。
underscore_d

0

我创建了一个简单的模板化函数Sequence(),用于生成数字序列。该功能遵循seq()R(链接)中的功能。关于此函数的好处是它可以生成各种数字序列和类型。

#include <iostream>
#include <vector>

template <typename T>
std::vector<T> Sequence(T min, T max, T by) {
  size_t n_elements = ((max - min) / by) + 1;
  std::vector<T> vec(n_elements);
  min -= by;
  for (size_t i = 0; i < vec.size(); ++i) {
    min += by;
    vec[i] = min;
  }
  return vec;
}

用法示例:

int main()
{
    auto vec = Sequence(0., 10., 0.5);
    for(auto &v : vec) {
        std::cout << v << std::endl;
    }
}

唯一的警告是所有数字都应为相同的推断类型。换句话说,对于双精度或浮点型,请为所有输入包括小数,如图所示。

更新日期:2018年6月14日


0

brainsandwich和underscore_d提出了很好的想法。由于填充是更改内容,因此STL算法中最简单的for_each()也应满足以下要求:

std::vector<int> v(10);
std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i++;});    

通用捕获[i=o]为lambda表达式赋予了不变性,并将其初始化为已知状态(在这种情况下为0)。关键字mutable允许每次调用lambda时更新此状态。

只需稍作修改即可获得正方形序列:

std::vector<int> v(10);
std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i*i; i++;});

生成类似R的seq不再困难,但是请注意,在R中,数字模式实际上是双精度的,因此实际上不需要参数化该类型。只需使用双。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.