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
std::iota
stattdessenstd::fill
(vorausgesetzt, Ihr Compiler ist neu genug, um ihn zu unterstützen).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 diesstd::vector
dem Konzept entspricht.Antworten:
Verwenden Sie vorzugsweise
std::iota
wie 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++11
Unterstützung haben (immer noch ein echtes Problem, wo ich arbeite), verwenden Siestd::generate
Folgendes: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.
quelle
iota
? (Sieht so aus, als hätte jemand das ebenso klare falsch geschriebenitoa
.)Sie sollten einen
std::iota
Algorithmus verwenden (definiert in<numeric>
):std::vector<int> ivec(100); std::iota(ivec.begin(), ivec.end(), 0); // ivec will become: [0..99]
Weil
std::fill
nur den Elementen im angegebenen Bereich der angegebene feste Wert zugewiesen wird[n1,n2)
. Undstd::iota
füllt den angegebenen Bereich[n1, n2)
mit sequentiell ansteigenden Werten, beginnend mit dem Anfangswert und dann mit.++value
Sie können auchstd::generate
als Alternative verwenden.Vergessen Sie nicht, dass dies der
std::iota
C ++ 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.aspxPS 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).quelle
iota
war vor mehr als 15 Jahren in der STL, daher haben einige Compiler sie immer unterstützt, lange vor C ++ 11Meine 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 sichcounting_iterator
selbst, wenn Sie es an verschiedenen Stellen benötigen. (Mit Boost können Sie atransform_iterator
von a verwendencounting_iterator
, um alle möglichen interessanten Sequenzen zu erstellen. Ohne Boost können Sie vieles von Hand erledigen, entweder in Form eines Generatorobjekttyps fürstd::generate
oder als etwas, das Sie in ein einstecken können handgeschriebener Zähliterator.)quelle
std::vector
wird im ersten Code aufgerufen? Muss der Bereichskonstruktor sein , ist aberboost::counting_iterator
implizit in einen InputIterator konvertierbar ?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
quelle
static
hat 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.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.
quelle
std::iota
, haben Sie sowieso, nein?it
undend
außerhalb der for-Schleife definiert sind. Irgendein Grund dafür?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.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; }
quelle
n
ein Mitglied des Lambda durch generalisierte Erfassung zu machen[n = 0]() mutable
, damit es den umgebenden Bereich nicht verschmutzt.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
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. :) :)
quelle
Apropos Boost:
auto ivec = boost::copy_range<std::vector<int>>(boost::irange(5, 10));
quelle
das funktioniert auch
j=0; for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){ *it = j++; }
quelle
In Bezug auf die Leistung sollten Sie den Vektor mit
reserve()
kombiniertenpush_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::generate
usw. 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 vonint
).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.
quelle
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:
quelle
Wenn Sie C ++ 98 wirklich verwenden möchten
std::fill
und 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; }
quelle
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, ...
quelle
_
für die dortige Implementierung reserviert sind.Ich habe eine einfache Vorlagenfunktion
Sequence()
zum Generieren von Zahlenfolgen erstellt. Die Funktionalität folgt derseq()
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
quelle
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üsselwortmutable
kann 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.
quelle