используйте std :: fill для заполнения вектора увеличивающимися числами

85

Я хотел бы заполнить vector<int>using std::fill, но вместо одного значения вектор должен содержать числа в порядке возрастания после.

Я попытался добиться этого, повторяя третий параметр функции на единицу, но это дало бы мне только векторы, заполненные 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(в любом случае, если ваш компилятор достаточно новый, чтобы его поддерживать).
Джерри Коффин
1
К сожалению, похоже, это часть нового стандарта (который я не должен использовать). Я видел, что в библиотеке BOOST есть такая функция, но она не принимает векторы ( boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/… ), но некоторые настраиваемые типы. А другого варианта нет?
BlackMamba
2
user1612880, если вы не можете использовать C ++ 11 / Boost, просто используйте код Лирана. Это не требование , что каждая операция должна быть на одной линии не существует общемировая нехватка символов доступны для файлов исходного кода C :-)
paxdiablo
Это было бы не из-за недостатка, а из-за производительности. Однако, если это невозможно сделать без жестоких взломов, я воспользуюсь решением, предоставленным Лираном.
BlackMamba
@ user1612880 А вы пробовали std::vector. Повышенная версия - это шаблон функции, а «имя типа» первого аргумента определяет концепцию. Трудно сказать, потому что я могу найти только очень формальную спецификацию, а не простое описание, но я думаю, что это std::vectorсоответствует концепции.
Джеймс Канце

Ответы:

128

Желательно использовать 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.
BoBTFish
источник
6
что вообще iotaозначает? (Похоже, что кто-то неправильно набрал одинаково ясно itoa.)
Люк Ашервуд,
9
Он ничего не «обозначает», это греческий эквивалент буквы i. Это имя, используемое для аналогичной функции в APL , и языке массивов, которое породило многие идеи в STL Степанова.
BoBTFish
1
Ага, спасибо! Хорошо, это скорее слово, чем инициализм; Возможно, мне больше повезло вспомнить это сейчас. В справочнике CPP говорилось об «Приращении начального значения» (обратите внимание на небольшое сходство), поэтому у меня в голове возникли инициалы. (И греческая связь не сразу очевидна при поиске в Google.) Спасибо также за историческую справку.
Люк Ашервуд,
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это алгоритм STL C ++ 11. Но многие современные компиляторы поддерживают его, например GCC, Clang и VS2012: http://msdn.microsoft.com/en-us/library/vstudio/jj651033.aspx

PS Эта функция названа в честь целочисленной функции из языка программирования APL и обозначает греческую букву йота. Я предполагаю, что изначально в APL это нечетное имя было выбрано потому, что оно напоминает “integer”(хотя в математике йота широко используется для обозначения мнимой части комплексного числа).

Александр Караберов
источник
1
Возможно, вы захотите добавить std :: iota из C ++ 11
hetepeperfan
@AlexanderKaraberov Но в большинстве случаев не используется последняя версия компилятора и нельзя использовать C ++ 11.
Джеймс Канце
1
iotaбыл в STL более 15 лет назад, поэтому некоторые компиляторы всегда поддерживали его, задолго до C ++ 11
Джонатан Уэйкли
2
Этот вектор будет иметь размер 0. Вам нужно будет добавить размер к контейнеру: std :: vector <int> ivec (100); std :: iota (ivec.begin (), ivec.end (), 0);
AKludges 03
14

Мой первый выбор (даже в 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 вы можете использовать a transform_iteratorиз a counting_iteratorдля создания всевозможных интересных последовательностей. Без Boost вы можете многое сделать вручную, либо в виде типа объекта-генератора std::generate, либо как что-то, что вы можете подключить к рукописный счетный итератор.)

Джеймс Канце
источник
В первой части кода, который конструктор из std::vectorвызывается? Должен быть конструктором диапазона , но может быть boost::counting_iteratorнеявно преобразован в InputIterator ?
CinCout
8

Я видел ответы с помощью std :: generate, но вы также можете «улучшить» это, используя статические переменные внутри лямбда, вместо объявления счетчика вне функции или создания класса генератора:

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

Я нахожу это немного более кратким

мозговой бутерброд
источник
5
staticздесь неправильная семантика, ИМО. Я бы использовал обобщенный захват, [i = 0]() mutableчтобы было ясно, что переменная привязана к конкретному экземпляру лямбда, а не к его сгенерированному типу класса. Трудно придумать ситуацию, при которой на практике будет различие, и это, вероятно, укажет на сомнительный дизайн, но в любом случае я думаю, что семантика лучше при использовании переменной-члена. Кроме того, это делает код более лаконичным; теперь тело лямбды может быть единственным оператором.
underscore_d
Да, так выглядит лучше; Я никогда раньше не видел, чтобы переменная инициализировалась в лямбда-захвате :)
brainsandwich
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
1
@bitmask Но для этого потребуется C ++ 11 :)
juanchopanza
2
Что довольно неинтуитивно и не оптимизировано, так это то, что itи endопределены вне цикла for. По какой причине?
Christian Rau
1
@FrerichRaabe Вторая причина звучит разумно (поскольку такая глупая ошибка VS может быть вообще разумной, хотя ее можно изменить в настройках проекта), но я не понимаю первую причину, что не так std::vector<int>::const_iterator it = vec.begin(), end = ivec.end()(ни повторяющийся набор, ни повторный звонок)? И строка длиннее, ну, это C ++, и вы все равно не обойдете случайный разрыв строки (а дни старых добрых 80-символьных дисплеев закончились). Но это дело вкуса, я думаю, и ты уже давно получил мой голос.
Christian Rau
2
@ChristianRau: Честно говоря: на практике я использую тот стиль, который используется в коде в файле, который я редактирую, то есть я ценю согласованность выше, чем преимущества или недостатки, о которых мы говорили до сих пор.
Frerich Raabe
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;
}
rashedcs
источник
Это очень элегантный ответ и, вероятно, самый лаконичный в общем случае.
Owl
1
IMO лучше сделать nчленом лямбды через обобщенный захват [n = 0]() mutable, поэтому он не загрязняет окружающую область.
underscore_d
Я пользуюсь этим. Может быть бесполезен для сложных ситуаций, но достаточно хорош для новичков в C ++. хорошее решение, если можно использовать лямбда.
Абинаш Даш
5

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
2

Кстати о наддуве:

auto ivec = boost::copy_range<std::vector<int>>(boost::irange(5, 10));
Михаил
источник
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::fill, std::generateи т.д. работает на диапазоне существующего векторных содержимого, и, следовательно, вектор должен быть заполнен некоторыми данными ранее. Даже если сделать следующее: std::vector<int> data(10);создает вектор со всеми элементами, установленными в значение по умолчанию (т.е. 0 в случае int).

Приведенный выше код позволяет избежать инициализации векторного содержимого перед его заполнением данными, которые вам действительно нужны. Производительность этого решения хорошо видна на больших наборах данных.

никто особенный
источник
2

Есть еще вариант - без йоты. Можно использовать выражение 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;});

Две важные вещи, почему это работает:

  1. Лямбда захватывает внешнюю область видимости [&], что означает, что я будет доступен внутри выражения
  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

Я знаю, что это старый вопрос, но сейчас я играю с библиотекой для решения именно этой проблемы. Требуется 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 включите десятичные дроби для всех входных данных, как показано.

Информация обновлена: 14 июня 2018 г.

Адам Эриксон
источник
0

brainsandwich и underscore_d дали очень хорошие идеи. Поскольку заполнение означает изменение содержимого, for_each (), самый простой из алгоритмов STL, также должен заполнить счет:

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-подобный seq не сложнее, но обратите внимание, что в R числовой режим на самом деле двойной, поэтому действительно нет необходимости параметризовать тип. Просто используйте двойной.

Winvicta
источник