Me gustaría completar un vector<int>
uso std::fill
, pero en lugar de un valor, el vector debe contener números en orden creciente después.
Intenté lograr esto iterando el tercer parámetro de la función en uno, pero esto solo me daría vectores llenos de 1 o 2 (dependiendo de la posición del ++
operador).
Ejemplo:
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
lugar destd::fill
(asumiendo que su compilador es lo suficientemente nuevo para admitirlo, de todos modos).std::vector
. La versión de refuerzo es una plantilla de función y el "nombre de tipo" del primer argumento especifica un concepto. Es difícil de decir, porque solo puedo encontrar una especificación muy formalista y ninguna descripción simple, pero creo que eso sestd::vector
ajusta al concepto.Respuestas:
Preferiblemente use
std::iota
así:std::vector<int> v(100) ; // vector with 100 ints. std::iota (std::begin(v), std::end(v), 0); // Fill with 0, 1, ..., 99.
Dicho esto, si no tiene ningún
c++11
soporte (sigue siendo un problema real donde trabajo), usestd::generate
así: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.
fuente
iota
, de todos modos? (Parece que alguien escribió mal lo igualmente claroitoa
.)Debes usar el
std::iota
algoritmo (definido en<numeric>
):std::vector<int> ivec(100); std::iota(ivec.begin(), ivec.end(), 0); // ivec will become: [0..99]
Porque
std::fill
simplemente asigna el valor fijo dado a los elementos en el rango dado[n1,n2)
. Ystd::iota
llena el rango dado[n1, n2)
con valores que aumentan secuencialmente, comenzando con el valor inicial y luego usando++value
.También puede usarstd::generate
como alternativa.No olvides que
std::iota
es el algoritmo STL de C ++ 11. Pero muchos compiladores modernos lo admiten, por ejemplo, GCC, Clang y VS2012: http://msdn.microsoft.com/en-us/library/vstudio/jj651033.aspxPS Esta función lleva el nombre de la función de número entero
⍳
del lenguaje de programación APL y significa una letra griega iota. Especulo que originalmente en APL se eligió este nombre extraño porque se parece a un“integer”
(aunque en matemáticas la iota se usa ampliamente para denotar la parte imaginaria de un número complejo).fuente
iota
estaba en STL hace más de 15 años, por lo que algunos compiladores siempre lo han admitido, mucho antes de C ++ 11Mi primera opción (incluso en C ++ 11) sería
boost::counting_iterator
:std::vector<int> ivec( boost::counting_iterator<int>( 0 ), boost::counting_iterator<int>( n ) );
o si el vector ya estaba construido:
std::copy( boost::counting_iterator<int>( 0 ), boost::counting_iterator<int>( ivec.size() ), ivec.begin() );
Si no puede usar Boost: tampoco
std::generate
(como se sugiere en otras respuestas), o impleméntelocounting_iterator
usted mismo, si lo necesita en varios lugares. (Con Boost, puede usar untransform_iterator
de acounting_iterator
para crear todo tipo de secuencias interesantes. Sin Boost, puede hacer mucho de esto a mano, ya sea en forma de un tipo de objeto generadorstd::generate
o como algo que puede conectar a un iterador de conteo escrito a mano.)fuente
std::vector
que se está llamando? ¿Tiene que ser el constructor de rango , pero esboost::counting_iterator
implícitamente convertible en un InputIterator ?He visto las respuestas con std :: generate pero también puede "mejorarlo" usando variables estáticas dentro de la lambda, en lugar de declarar un contador fuera de la función o crear una clase generadora:
std::vector<int> vec; std::generate(vec.begin(), vec.end(), [] { static int i = 0; return i++; });
Lo encuentro un poco mas conciso
fuente
static
tiene la semántica incorrecta aquí, en mi opinión. Usaría una captura generalizada[i = 0]() mutable
para que quede claro que la variable tiene como alcance la instancia específica de lambda, no su tipo de clase generada. Es difícil idear una situación en la que habría una diferencia en la práctica, y eso probablemente indicaría un diseño cuestionable, pero en cualquier caso, creo que la semántica es superior al usar una variable miembro. Además, crea un código más conciso; ahora el cuerpo de lambda puede ser una sola declaración.Si prefiere no utilizar las funciones de C ++ 11, puede utilizar
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; } }
Este programa imprime de 0 a 9.
fuente
std::iota
todos modos, ¿no?it
yend
se definen fuera del ciclo for. ¿Alguna razón para esto?std::vector<int>::const_iterator it = vec.begin(), end = ivec.end()
(ni la escritura repetida ni llamada repetida)? Y la línea es más larga, bueno, es C ++ y de todos modos no saldrás de línea ocasionalmente (y los días de las buenas pantallas de 80 caracteres se acabaron). Pero es una cuestión de gustos, supongo, y de todos modos obtuviste mi voto a favor hace mucho tiempo.Podemos usar la función de generar que existe en el archivo de encabezado del algoritmo.
Fragmento de código :
#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; }
fuente
n
un miembro de la lambda a través de la captura generalizada[n = 0]() mutable
, por lo que no contamina el alcance circundante.std :: iota está limitado a una secuencia n, n + 1, n + 2, ...
Pero, ¿qué sucede si desea llenar una matriz con una secuencia genérica f (0), f (1), f (2), etc.? A menudo, podemos evitar un generador de seguimiento de estados. Por ejemplo,
int a[7]; auto f = [](int x) { return x*x; }; transform(a, a+7, a, [a, f](int &x) {return f(&x - a);});
producirá la secuencia de cuadrados
0 1 4 9 16 25 36
Sin embargo, este truco no funcionará con otros contenedores.
Si está atascado con C ++ 98, puede hacer cosas horribles como:
int f(int &x) { int y = (int) (long) &x / sizeof(int); return y*y; }
y entonces
int a[7]; transform((int *) 0, ((int *) 0) + 7, a, f);
Pero no lo recomendaría. :)
fuente
esto también funciona
j=0; for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){ *it = j++; }
fuente
En términos de rendimiento, debe inicializar el vector con el uso de funciones
reserve()
combinadaspush_back()
como en el siguiente ejemplo:const int numberOfElements = 10; std::vector<int> data; data.reserve(numberOfElements); for(int i = 0; i < numberOfElements; i++) data.push_back(i);
Toda la
std::fill
,std::generate
etc., están operando en el rango de contenido vectorial existente y, por lo tanto, el vector debe ser llenado con algunos datos antes. Incluso haciendo lo siguiente:std::vector<int> data(10);
crea un vector con todos los elementos establecidos en su valor predeterminado (es decir, 0 en el caso deint
).El código anterior evita inicializar el contenido vectorial antes de llenarlo con los datos que realmente desea. El rendimiento de esta solución es bien visible en grandes conjuntos de datos.
fuente
Hay otra opción, sin usar iota. Se puede utilizar la expresión 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;});
Dos cosas importantes por las que funciona:
fuente
Si realmente quiere usar
std::fill
y está limitado a C ++ 98, puede usar algo como lo siguiente,#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; }
fuente
Hablando de impulso:
auto ivec = boost::copy_range<std::vector<int>>(boost::irange(5, 10));
fuente
Sé que esta es una pregunta antigua, pero actualmente estoy jugando con la biblioteca para manejar exactamente este problema. Requiere 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, ...
fuente
_
están reservados para la implementación allí.Creé una función simple con plantilla
Sequence()
, para generar secuencias de números. La funcionalidad sigue a laseq()
función en R ( enlace ). Lo bueno de esta función es que funciona para generar una variedad de secuencias y tipos de números.#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; }
Uso de ejemplo:
int main() { auto vec = Sequence(0., 10., 0.5); for(auto &v : vec) { std::cout << v << std::endl; } }
La única advertencia es que todos los números deben ser del mismo tipo inferido. En otras palabras, para dobles o flotantes, incluya decimales para todas las entradas, como se muestra.
Actualizado: 14 de junio de 2018
fuente
brainsandwich y underscore_d dieron muy buenas ideas. Dado que llenar es cambiar el contenido, for_each (), el más simple entre los algoritmos STL, también debería llenar la factura:
std::vector<int> v(10); std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i++;});
La captura generalizada
[i=o]
imparte a la expresión lambda un invariante y la inicializa a un estado conocido (en este caso 0). la palabra clavemutable
permite que este estado se actualice cada vez que se llama a lambda.Solo se necesita una pequeña modificación para obtener una secuencia de cuadrados:
std::vector<int> v(10); std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i*i; i++;});
Generar una secuencia similar a R no es más difícil, pero tenga en cuenta que en R, el modo numérico es en realidad doble, por lo que realmente no es necesario parametrizar el tipo. Solo usa doble.
fuente