std :: fillを使用して、ベクトルに増加する数値を入力します


82

vector<int>usingを入力したいのですstd::fillが、1つの値の代わりに、ベクトルには後に昇順で数値を含める必要があります。

関数の3番目のパラメーターを1つ繰り返すことでこれを達成しようとしましたが、これでは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します(とにかく、コンパイラがそれをサポートするのに十分新しいと仮定します)。
ジェリーコフィン2013

1
残念ながら、これは新しい標準の一部のようです(私は使用してはいけません)。BOOSTライブラリにそのような関数があるのを見ましたが、ベクトル(boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/…)を受け入れませんが、いくつかのカスタムタイプを受け入れます。別のオプションはありませんか?
ブラックマンバ2013

2
user1612880、C ++ 11 / Boostを使用できない場合は、Liranのコードを使用してください。すべての操作が1行である必要はなく、Cソースコードファイルで使用できる文字が世界的に不足している必要もありません:-)
paxdiablo 2013

不足ではなく、パフォーマンスのためでした。ただし、残酷なハッキングなしにこれを可能にする方法がない場合は、Liranが提供するソリューションを使用します。
ブラックマンバ2013

@ user1612880で試してみましたかstd::vector。ブーストバージョンは関数テンプレートであり、最初の引数の「タイプ名」は概念を指定します。非常に形式的な仕様しかなく、簡単な説明も見つからないのでわかりにくいですが、それstd::vectorはコンセプトに合っていると思います。
James Kanze 2013

回答:


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ます。)
Luke Usherwood 2017

9
それは何の意味もありません。ギリシャ語で文字iに相当します。これは、APLの同様の機能に使用される名前であり、StepanovのSTLで多くのアイデアを生み出した配列言語です。
BoBTFish 2017

1
あははありがとう!OK、頭字語ではなく単語です。私は今それを覚えている方が幸運かもしれません。CPPリファレンスは「開始値の増分」(わずかな類似性に注意)について話していたので、頭の中でイニシャルを取得しました。(そして、ギリシャのつながりはグーグルによってすぐには明らかではありません。)歴史的な参照にも感謝します。
ルークアッシャーウッド2017

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)を、初期値から始めて、を使用して、順番に増加する値で埋めます。代替として++value使用することもできstd::generateます。

それstd::iotaがC ++ 11STLアルゴリズムであることを忘れないでください。しかし、GCC、Clang、VS2012などの最新のコンパイラの多くがこれをサポートしています:http://msdn.microsoft.com/en-us/library/vstudio/jj651033.aspx

PSこの関数は、プログラミング言語APLの整数関数にちなんで名付けられ、ギリシャ文字のiotaを意味します。もともとAPLでは、この奇妙な名前がに似ているために選択されたと推測し“integer”ます(数学では、iotaは複素数の虚数部を表すために広く使用されていますが)。


1
std :: iotaはC ++ 11からのものです
hetepeperfan 2013

@AlexanderKaraberovしかし、ほとんどの場所では最新バージョンのコンパイラが使用されておらず、C ++ 11を使用できません。
James Kanze 2013

1
iota15年以上前に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と、のcounting_iteratorを使用してあらゆる種類の興味深いシーケンスを作成できます。Boostを使用しない場合は、のジェネレータオブジェクトタイプの形式でstd::generate、またはプラグインできるものとして、これの多くを手動で行うことができます。手書きのカウントイテレータ。)


コードの最初の部分では、そのコンストラクタのがstd::vector呼び出されていますか?する必要がある範囲のコンストラクタが、あるboost::counting_iteratorに暗黙的に変換InputIterator
CinCout 2018年

8

std :: generateで答えを見てきましたが、関数の外部でカウンターを宣言したり、ジェネレータークラスを作成したりする代わりに、ラムダ内の静的変数を使用してそれを「改善」することもできます。

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

もう少し簡潔だと思います


5
staticここで間違ったセマンティクスを持っています、IMO。[i = 0]() mutable変数が生成されたクラスタイプではなく、ラムダの特定のインスタンスにスコープされていることが明確になるように、一般化されたキャプチャを使用します。実際に違いがある状況を考えるのは難しいですし、それはおそらく疑わしい設計を示しているでしょうが、いずれにせよ、メンバー変数を使用するとセマンティクスが優れていると思います。さらに、より簡潔なコードになります。これで、ラムダの本体を1つのステートメントにすることができます。
underscore_d

ええ、それは良く見えます; ラムダキャプチャで初期化された変数を見たことがありませんでした:)
brainsandwich 2018年

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確かに、ラムダがある場合は、std::iotaとにかく持っていますね。
BoBTFish 2013

1
@bitmaskしかし、それにはC ++ 11が必要になります:)
juanchopanza 2013

2
非常に直感的でなく、合理化されていないのは、itendがforループの外側で定義されているという事実です。これには何か理由がありますか?
クリスチャンラウ2013

1
@FrerichRaabe 2番目の理由は合理的に聞こえます(そのような愚かなVSバグがまったく合理的である限り、プロジェクトの設定で変更可能です)が、最初の理由はわかりません、何が問題なのですかstd::vector<int>::const_iterator it = vec.begin(), end = ivec.end()(繰り返し入力も繰り返し呼び出し)?そして、行は長くなります、まあ、それはC ++であり、とにかく時々改行を回避することはありません(そして古き良き80文字のディスプレイの時代は終わりました)。しかし、それは私が推測する好みの問題であり、とにかく、あなたはずっと前に私の賛成を得ました。
クリスチャンラウ2013

2
@ChristianRau:正直に言うと、実際には、編集しているファイルのコードが使用するスタイルを使用します。つまり、これまでに説明した長所または短所よりも一貫性を高く評価します。
Frerich Raabe 2013

6

アルゴリズムヘッダーファイルに存在する生成関数を使用できます。

コードスニペット :

#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、周囲のスコープを汚染しません。
underscore_d

これを使っています。高度な状況には役立たないかもしれませんが、C ++の初心者には十分です。ラムダを使用できる場合は良い解決策です。
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を使用できません(ランバスなし)
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 +ラムダ式を使用できます。

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

それが機能している理由の2つの重要なこと:

  1. Lambdaは外部スコープ[&]をキャプチャします。これは、iが式の内部で使用できることを意味します。
  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
うわぁ。これは非常に読みにくいコードであり、書かれているようにグローバルスコープでも無効になります。これは、で始まる識別子_がそこでの実装に予約されているためです。
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;
    }
}

唯一の注意点は、すべての数値が同じ推定型である必要があるということです。つまり、doubleまたはfloatの場合、示されているように、すべての入力に小数を含めます。

更新日: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]は、ラムダ式に不変条件を付与し、既知の状態(この場合は0)に初期化します。キーワードをmutable使用すると、ラムダが呼び出されるたびにこの状態を更新できます。

一連の正方形を取得するには、わずかな変更が必要です。

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

Rのようなシーケンスを生成することはこれ以上難しいことではありませんが、Rでは数値モードが実際には2倍であるため、型をパラメーター化する必要はありません。ダブルを使用するだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.