Verwenden Sie std :: fill, um den Vektor mit zunehmenden Zahlen zu füllen

85

Ich möchte ein vector<int>using ausfüllen std::fill, aber anstelle eines Wertes sollte der Vektor Zahlen in aufsteigender Reihenfolge enthalten.

Ich habe versucht, dies zu erreichen, indem ich den dritten Parameter der Funktion um eins iterierte, aber dies würde mir nur Vektoren geben, die mit 1 oder 2 gefüllt sind (abhängig von der Position des ++Operators).

Beispiel:

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
Schwarze Mamba
quelle
25
Verwenden Sie std::iotastattdessen std::fill(vorausgesetzt, Ihr Compiler ist neu genug, um ihn zu unterstützen).
Jerry Coffin
1
Leider scheint dies Teil des neuen Standards zu sein (den ich nicht verwenden darf). Ich habe gesehen, dass die BOOST-Bibliothek eine solche Funktion hat, aber keine Vektoren ( boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/… ) akzeptiert, sondern einen benutzerdefinierten Typ. Gibt es nicht eine andere Option?
BlackMamba
2
user1612880, wenn Sie C ++ 11 / Boost nicht verwenden können, verwenden Sie einfach Lirans Code. Es ist nicht erforderlich, dass jede Operation in einer einzigen Zeile ausgeführt werden muss, und es gibt auch keinen weltweiten Mangel an Zeichen, die für C-Quellcodedateien verfügbar sind :-)
paxdiablo
Es wäre nicht für Mangel gewesen, sondern für Leistung. Wenn es jedoch keine Möglichkeit gibt, dies ohne grausame Hacks zu ermöglichen, werde ich die von Liran bereitgestellte Lösung verwenden.
BlackMamba
@ user1612880 Hast du es mit versucht std::vector. Die Boost-Version ist eine Funktionsvorlage, und der "Typname" des ersten Arguments gibt ein Konzept an. Es ist schwer zu sagen, weil ich nur eine sehr formalistische Spezifikation und keine einfache Beschreibung finden kann, aber ich denke, dass dies std::vectordem Konzept entspricht.
James Kanze

Antworten:

127

Verwenden Sie vorzugsweise std::iotawie folgt:

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

Das heißt, wenn Sie keine c++11Unterstützung haben (immer noch ein echtes Problem, wo ich arbeite), verwenden Sie std::generateFolgendes:

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.
BoBTFish
quelle
6
Wofür um alles in der Welt steht das überhaupt iota? (Sieht so aus, als hätte jemand das ebenso klare falsch geschrieben itoa.)
Luke Usherwood
9
Es "steht" für nichts, es ist das griechische Äquivalent des Buchstabens i. Es ist der Name, der für eine ähnliche Funktion in APL und Array-Sprache verwendet wird, die viele der Ideen in Stepanovs STL ausgelöst hat.
BoBTFish
1
Ah-ha danke! OK, es ist eher ein Wort als ein Initialismus. Ich könnte jetzt besseres Glück haben, wenn ich mich daran erinnere. CPP Reference sprach über "Inkremente des Startwerts" (beachten Sie die leichte Ähnlichkeit), so dass ich Initialen in meinem Kopf bekam. (Und die griechische Verbindung ist für Googeln nicht sofort ersichtlich.) Danke auch für den historischen Hinweis.
Luke Usherwood
47

Sie sollten einen std::iotaAlgorithmus verwenden (definiert in <numeric>):

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

Weil std::fillnur den Elementen im angegebenen Bereich der angegebene feste Wert zugewiesen wird [n1,n2). Und std::iotafüllt den angegebenen Bereich [n1, n2)mit sequentiell ansteigenden Werten, beginnend mit dem Anfangswert und dann mit. ++valueSie können auch std::generateals Alternative verwenden.

Vergessen Sie nicht, dass dies der std::iotaC ++ 11 STL-Algorithmus ist. Viele moderne Compiler unterstützen dies jedoch, z. B. GCC, Clang und VS2012: http://msdn.microsoft.com/en-us/library/vstudio/jj651033.aspx

PS Diese Funktion ist nach der Ganzzahlfunktion aus der Programmiersprache APL benannt und kennzeichnet einen griechischen Buchstaben iota. Ich spekuliere, dass dieser ungerade Name ursprünglich in APL gewählt wurde, weil er einem ähnelt “integer”(obwohl in der Mathematik iota häufig verwendet wird, um den Imaginärteil einer komplexen Zahl zu bezeichnen).

Oleksandr Karaberov
quelle
1
Vielleicht möchten Sie std :: iota hinzufügen, ist von C ++ 11
hetepeperfan
@AlexanderKaraberov Aber die meisten Orte verwenden nicht die neueste Version des Compilers und können C ++ 11 nicht verwenden.
James Kanze
1
iotawar vor mehr als 15 Jahren in der STL, daher haben einige Compiler sie immer unterstützt, lange vor C ++ 11
Jonathan Wakely
2
Dieser Vektor hat die Größe 0. Sie müssten dem Container eine Größe hinzufügen: std :: vector <int> ivec (100); std :: iota (ivec.begin (), ivec.end (), 0);
AKludges
14

Meine erste Wahl (auch in C ++ 11) wäre boost::counting_iterator:

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

oder wenn der Vektor bereits konstruiert wurde:

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

Wenn Sie Boost nicht verwenden können: entweder std::generate(wie in anderen Antworten vorgeschlagen) oder implementieren Sie sich counting_iteratorselbst, wenn Sie es an verschiedenen Stellen benötigen. (Mit Boost können Sie a transform_iteratorvon a verwenden counting_iterator, um alle möglichen interessanten Sequenzen zu erstellen. Ohne Boost können Sie vieles von Hand erledigen, entweder in Form eines Generatorobjekttyps für std::generateoder als etwas, das Sie in ein einstecken können handgeschriebener Zähliterator.)

James Kanze
quelle
Welcher Konstruktor von std::vectorwird im ersten Code aufgerufen? Muss der Bereichskonstruktor sein , ist aber boost::counting_iteratorimplizit in einen InputIterator konvertierbar ?
CinCout
8

Ich habe die Antworten mit std :: generate gesehen, aber Sie können dies auch "verbessern", indem Sie statische Variablen innerhalb des Lambda verwenden, anstatt einen Zähler außerhalb der Funktion zu deklarieren oder eine Generatorklasse zu erstellen:

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

Ich finde es etwas prägnanter

Brainsandwich
quelle
5
statichat hier die falsche Semantik, IMO. Ich würde eine verallgemeinerte Erfassung verwenden [i = 0]() mutable, damit klar ist, dass die Variable auf die spezifische Instanz des Lambda beschränkt ist, nicht auf den generierten Klassentyp. Es ist schwer, eine Situation zu erfinden, in der es einen Unterschied in der Praxis geben würde, und das würde wahrscheinlich auf ein fragwürdiges Design hinweisen, aber auf jeden Fall denke ich, dass die Semantik bei Verwendung einer Mitgliedsvariablen überlegen ist. Außerdem sorgt es für einen präziseren Code. Jetzt kann der Körper des Lambda eine einzige Aussage sein.
underscore_d
Ja das sieht besser aus; Ich hatte noch nie zuvor eine Variable gesehen, die in der Lambda-Erfassung initialisiert wurde :)
Brainsandwich
6

Wenn Sie C ++ 11-Funktionen lieber nicht verwenden möchten, können Sie Folgendes verwenden 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;
    }
}

Dieses Programm druckt 0 bis 9.

Frerich Raabe
quelle
3
@bitmask Sicherlich, wenn Sie Lambdas haben std::iota, haben Sie sowieso, nein?
BoBTFish
1
@bitmask Aber das würde C ++ 11 erfordern :)
Juanchopanza
2
Was ziemlich unintuitiv und nicht rationalisiert ist, ist die Tatsache, dass itund endaußerhalb der for-Schleife definiert sind. Irgendein Grund dafür?
Christian Rau
1
@FrerichRaabe Der zweite Grund klingt vernünftig (soweit ein so dummer VS-Fehler überhaupt vernünftig sein kann, kann er in den Projekteinstellungen geändert werden), aber ich verstehe nicht den ersten Grund, was falsch ist std::vector<int>::const_iterator it = vec.begin(), end = ivec.end()(weder wiederholte Eingabe noch wiederholter Anruf)? Und die Zeile ist länger, na ja, es ist C ++ und Sie werden sowieso keinen gelegentlichen Zeilenumbruch umgehen (und die Zeiten guter alter 80-Zeichen-Anzeigen sind vorbei). Aber es ist Geschmackssache, denke ich, und du hast sowieso schon vor langer Zeit meine Gegenstimme bekommen.
Christian Rau
2
@ChristianRau: Um ganz ehrlich zu sein: In der Praxis verwende ich jeden Stil, den der Code in der Datei, die ich bearbeite, verwendet, dh ich schätze die Konsistenz höher als und der bisher erwähnten Vor- oder Nachteile.
Frerich Raabe
6

Wir können die Generierungsfunktion verwenden, die in der Algorithmus-Header-Datei vorhanden ist.

Code-Auszug :

#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;
}
Hautausschläge
quelle
Dies ist eine sehr elegante Antwort und im allgemeinen Fall wahrscheinlich die prägnanteste.
Eule
1
IMO ist es überlegen, nein Mitglied des Lambda durch generalisierte Erfassung zu machen [n = 0]() mutable, damit es den umgebenden Bereich nicht verschmutzt.
underscore_d
Ich benutze diesen. Möglicherweise nicht nützlich für fortgeschrittene Situationen, aber gut genug für C ++ - Anfänger. gute lösung wenn man lambda verwenden kann.
Abinash Dash
5

std :: iota ist auf eine Folge n, n + 1, n + 2, ... beschränkt

Was aber, wenn Sie ein Array mit einer generischen Sequenz f (0), f (1), f (2) usw. füllen möchten? Oft können wir einen Zustandsverfolgungsgenerator vermeiden. Zum Beispiel,

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

erzeugt die Folge von Quadraten

0 1 4 9 16 25 36

Dieser Trick funktioniert jedoch nicht mit anderen Containern.

Wenn Sie mit C ++ 98 nicht weiterkommen, können Sie schreckliche Dinge tun wie:

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

und dann

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

Aber ich würde es nicht empfehlen. :) :)

Pururu
quelle
1
OP kann C ++ 11 (keine Lambas) nicht verwenden
yizzlez
2

Apropos Boost:

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

das funktioniert auch

j=0;
for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){
    *it = j++;
}
Keine Idee für den Namen
quelle
2
Natürlich funktioniert das manuelle Herumstöbern mit Iteratoren, aber wenn das OP dies tun wollte, hätten sie nicht nach einem stdlib-Algorithmus gefragt, oder?
underscore_d
2

In Bezug auf die Leistung sollten Sie den Vektor mit reserve()kombinierten push_back()Funktionen wie im folgenden Beispiel initialisieren :

const int numberOfElements = 10;

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

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

Alle std::fill, std::generateusw. werden auf Palette bestehenden Vektorinhaltes betrieben wird , und daher muss der Vektor mit einigen Daten früher gefüllt werden. Selbst wenn Sie Folgendes tun: std::vector<int> data(10);Erstellt einen Vektor, bei dem alle Elemente auf ihren Standardwert gesetzt sind (dh 0 im Fall von int).

Mit dem obigen Code wird vermieden, dass Vektorinhalte initialisiert werden, bevor sie mit den gewünschten Daten gefüllt werden. Die Leistung dieser Lösung ist bei großen Datenmengen gut sichtbar.

niemand besonderes
quelle
2

Es gibt noch eine andere Option - ohne iota zu verwenden. Der Ausdruck For_each + Lambda kann verwendet werden:

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

Zwei wichtige Dinge, warum es funktioniert:

  1. Lambda erfasst den äußeren Bereich [&], was bedeutet, dass ich innerhalb des Ausdrucks verfügbar sein werde
  2. Das als Referenz übergebene Element ist daher innerhalb des Vektors veränderbar
Egor Wexler
quelle
1

Wenn Sie C ++ 98 wirklich verwenden möchten std::fillund darauf beschränkt sind, können Sie Folgendes verwenden:

#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;
}
König Kokonut
quelle
1

Ich weiß, dass dies eine alte Frage ist, aber ich spiele gerade mit der Bibliothek , um genau dieses Problem zu lösen. Es erfordert 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, ...
Dawid
quelle
1
Huch. Das ist ein sehr schwer zu lesender Code, und er wäre auch im globalen Bereich ungültig, wie geschrieben, da Bezeichner, die mit beginnen, _für die dortige Implementierung reserviert sind.
underscore_d
0

Ich habe eine einfache Vorlagenfunktion Sequence()zum Generieren von Zahlenfolgen erstellt. Die Funktionalität folgt der seq()Funktion in R ( Link ). Das Schöne an dieser Funktion ist, dass sie eine Vielzahl von Zahlenfolgen und -typen generiert.

#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;
}

Anwendungsbeispiel:

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

Die einzige Einschränkung ist, dass alle Zahlen vom gleichen abgeleiteten Typ sein sollten. Mit anderen Worten, schließen Sie für Double oder Floats Dezimalstellen für alle Eingaben ein, wie gezeigt.

Aktualisiert: 14. Juni 2018

Adam Erickson
quelle
0

brainsandwich und underscore_d gaben sehr gute Ideen. Da zum Füllen der Inhalt geändert werden muss, sollte for_each (), der einfachste unter den STL-Algorithmen, auch die Rechnung füllen:

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

Die verallgemeinerte Erfassung [i=o]verleiht dem Lambda-Ausdruck eine Invariante und initialisiert ihn in einen bekannten Zustand (in diesem Fall 0). Mit dem Schlüsselwort mutablekann dieser Status bei jedem Aufruf von Lambda aktualisiert werden.

Es ist nur eine geringfügige Änderung erforderlich, um eine Folge von Quadraten zu erhalten:

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

Es ist nicht schwieriger, eine R-ähnliche Sequenz zu generieren. Beachten Sie jedoch, dass der numerische Modus in R tatsächlich doppelt so hoch ist, sodass der Typ nicht unbedingt parametrisiert werden muss. Verwenden Sie einfach doppelt.

winvicta
quelle