use std :: fill para poblar el vector con números crecientes

82

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
Mamba negro
fuente
25
Úselo en std::iotalugar de std::fill(asumiendo que su compilador es lo suficientemente nuevo para admitirlo, de todos modos).
Jerry Coffin
1
Desafortunadamente, esto parece ser parte del nuevo estándar (que no debo usar). Vi que la biblioteca BOOST tiene tal función, pero no acepta vectores ( boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/… ) pero algunos tipos personalizados. ¿No hay otra opción?
BlackMamba
2
user1612880, si no puede usar C ++ 11 / Boost, simplemente use el código de Liran. No es un requisito que todas las operaciones tengan que estar en una sola línea ni hay una escasez mundial de caracteres disponibles para los archivos de código fuente C :-)
paxdiablo
No habría sido por escasez sino por rendimiento. Sin embargo, si no hay forma de hacerlo posible sin hacks crueles, usaré la solución proporcionada por Liran.
BlackMamba
@ user1612880 ¿Lo has probado con 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 se std::vectorajusta al concepto.
James Kanze

Respuestas:

125

Preferiblemente use std::iotaasí:

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++11soporte (sigue siendo un problema real donde trabajo), use std::generateasí:

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
fuente
6
¿Qué diablos significa iota, de todos modos? (Parece que alguien escribió mal lo igualmente claro itoa.)
Luke Usherwood
9
No "representa" nada, es el equivalente griego de la letra i. Es el nombre que se usa para una función similar en APL , y el lenguaje de matriz que provocó muchas de las ideas en STL de Stepanov.
BoBTFish
1
¡Ah-ha gracias! Está bien, entonces es una palabra en lugar de un inicialismo; Puede que tenga más suerte recordando eso ahora. CPP Reference habló sobre "Incrementos del valor inicial" (tenga en cuenta la ligera similitud), así que tengo iniciales en mi cabeza. (Y la conexión griega no es inmediatamente obvia al buscar en Google). Gracias por la referencia histórica también.
Luke Usherwood
47

Debes usar el std::iotaalgoritmo (definido en <numeric>):

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

Porque std::fillsimplemente asigna el valor fijo dado a los elementos en el rango dado [n1,n2). Y std::iotallena el rango dado [n1, n2)con valores que aumentan secuencialmente, comenzando con el valor inicial y luego usando ++value.También puede usar std::generatecomo alternativa.

No olvides que std::iotaes 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.aspx

PS 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).

Oleksandr Karaberov
fuente
1
Es posible que desee agregar std :: iota es de C ++ 11
hetepeperfan
@AlexanderKaraberov Pero la mayoría de los lugares no usan la última versión del compilador y no pueden usar C ++ 11.
James Kanze
1
iotaestaba en STL hace más de 15 años, por lo que algunos compiladores siempre lo han admitido, mucho antes de C ++ 11
Jonathan Wakely
2
Ese vector tendrá un tamaño 0. Debería agregar un tamaño al contenedor: std :: vector <int> ivec (100); std :: iota (ivec.begin (), ivec.end (), 0);
AKludges
13

Mi 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éntelo counting_iteratorusted mismo, si lo necesita en varios lugares. (Con Boost, puede usar un transform_iteratorde a counting_iteratorpara crear todo tipo de secuencias interesantes. Sin Boost, puede hacer mucho de esto a mano, ya sea en forma de un tipo de objeto generador std::generateo como algo que puede conectar a un iterador de conteo escrito a mano.)

James Kanze
fuente
En la primera pieza de código, que el constructor de la std::vectorque se está llamando? ¿Tiene que ser el constructor de rango , pero es boost::counting_iteratorimplícitamente convertible en un InputIterator ?
CinCout
8

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

sandwich de cerebro
fuente
5
statictiene la semántica incorrecta aquí, en mi opinión. Usaría una captura generalizada [i = 0]() mutablepara 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.
underscore_d
Sí, eso se ve mejor; Nunca había visto una variable inicializada en la captura lambda antes :)
brainsandwich
6

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.

Frerich Raabe
fuente
3
@bitmask Seguramente si tienes lambdas las tienes de std::iotatodos modos, ¿no?
BoBTFish
1
@bitmask Pero eso requeriría C ++ 11 :)
juanchopanza
2
Lo que es bastante poco intuitivo y no simplificado es el hecho de que ity endse definen fuera del ciclo for. ¿Alguna razón para esto?
Christian Rau
1
@FrerichRaabe La segunda razón suena razonable (en la medida en que un error VS tan estúpido puede ser razonable en absoluto, sin embargo, se puede cambiar en la configuración del proyecto), pero no entiendo la primera razón, ¿qué pasa con 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.
Christian Rau
2
@ChristianRau: Para ser totalmente honesto: en la práctica utilizo cualquier estilo que use el código en el archivo que estoy editando, es decir, valoro más la coherencia que las ventajas o desventajas que mencionamos hasta ahora.
Frerich Raabe
6

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;
}
rashedcs
fuente
Esta es una respuesta muy elegante y probablemente la más concisa en el caso general.
Búho
1
En mi opinión, es superior hacer nun miembro de la lambda a través de la captura generalizada [n = 0]() mutable, por lo que no contamina el alcance circundante.
underscore_d
Estoy usando este. Puede que no sea útil para situaciones avanzadas, pero lo suficientemente bueno para principiantes de C ++. buena solución si se puede usar lambda.
Abinash Dash
4

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. :)

pururu
fuente
1
OP no puede usar C ++ 11 (sin lambas)
yizzlez
2

esto también funciona

j=0;
for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){
    *it = j++;
}
No tengo idea de nombre
fuente
2
Por supuesto, hurgar manualmente con iteradores funciona, pero si el OP quisiera hacer eso, no habrían preguntado sobre un algoritmo stdlib, ¿verdad?
underscore_d
2

En términos de rendimiento, debe inicializar el vector con el uso de funciones reserve()combinadas push_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::generateetc., 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 de int).

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.

nadie especial
fuente
2

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:

  1. Lambda captura el alcance externo [&], lo que significa que estaré disponible dentro de la expresión
  2. elemento pasado como referencia, por lo tanto, es mutable dentro del vector
Egor Wexler
fuente
1

Si realmente quiere usar std::filly 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;
}
Rey Kokonut
fuente
1

Hablando de impulso:

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

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, ...
Dawid
fuente
1
¡Ay! Ese es un código muy difícil de leer, y también sería inválido en el alcance global, como está escrito, porque los identificadores que comienzan con _están reservados para la implementación allí.
underscore_d
0

Creé una función simple con plantilla Sequence(), para generar secuencias de números. La funcionalidad sigue a la seq()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

Adam Erickson
fuente
0

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 clave mutablepermite 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.

winvicta
fuente