Chciałbym wypełnić vector<int>
użycie std::fill
, ale zamiast jednej wartości wektor powinien zawierać liczby w kolejności rosnącej po.
Próbowałem to osiągnąć, iterując trzeci parametr funkcji o jeden, ale to dałoby mi tylko wektory wypełnione 1 lub 2 (w zależności od pozycji ++
operatora).
Przykład:
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
zamiaststd::fill
(zakładając, że Twój kompilator jest wystarczająco nowy, aby go obsługiwać).std::vector
. Wersja przyspieszenia jest szablonem funkcji, a „nazwa typu” pierwszego argumentu określa koncepcję. Trudno powiedzieć, ponieważ mogę znaleźć tylko bardzo formalistyczną specyfikację, a nie prosty opis, ale myślę, żestd::vector
jest to zgodne z koncepcją.Odpowiedzi:
Najlepiej używać w
std::iota
ten sposób:std::vector<int> v(100) ; // vector with 100 ints. std::iota (std::begin(v), std::end(v), 0); // Fill with 0, 1, ..., 99.
To powiedziawszy, jeśli nie masz żadnego
c++11
wsparcia (nadal jest to prawdziwy problem w miejscu pracy), użyj wstd::generate
ten sposób: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.
źródło
iota
oznacza? (Wygląda na to, że ktośitoa
Powinieneś użyć
std::iota
algorytmu (zdefiniowanego w<numeric>
):std::vector<int> ivec(100); std::iota(ivec.begin(), ivec.end(), 0); // ivec will become: [0..99]
Ponieważ
std::fill
po prostu przypisuje daną stałą wartość do elementów w podanym zakresie[n1,n2)
. Istd::iota
wypełnia podany zakres[n1, n2)
kolejno rosnącymi wartościami, zaczynając od wartości początkowej, a następnie używając++value
.Możesz również użyćstd::generate
jako alternatywy.Nie zapominaj, że
std::iota
jest to algorytm C ++ 11 STL. Ale obsługuje go wiele nowoczesnych kompilatorów, np. GCC, Clang i VS2012: http://msdn.microsoft.com/en-us/library/vstudio/jj651033.aspxPS Ta funkcja została nazwana na cześć funkcji całkowitej
⍳
z języka programowania APL i oznacza grecką literę iota. Przypuszczam, że pierwotnie w APL ta dziwna nazwa została wybrana, ponieważ przypomina“integer”
(chociaż w matematyce jota jest powszechnie używana do oznaczania urojonej części liczby zespolonej).źródło
iota
był w STL ponad 15 lat temu, więc niektórzy kompilatorzy zawsze go wspierali, na długo przed C ++ 11Moim pierwszym wyborem (nawet w C ++ 11) byłoby
boost::counting_iterator
:std::vector<int> ivec( boost::counting_iterator<int>( 0 ), boost::counting_iterator<int>( n ) );
lub jeśli wektor został już skonstruowany:
std::copy( boost::counting_iterator<int>( 0 ), boost::counting_iterator<int>( ivec.size() ), ivec.begin() );
Jeśli nie możesz użyć Boost: albo
std::generate
(jak sugerowano w innych odpowiedziach), albo wdrożyćcounting_iterator
samemu, jeśli potrzebujesz go w różnych miejscach. (Z Boost możesz użyć atransform_iterator
z acounting_iterator
do tworzenia różnego rodzaju interesujących sekwencji. Bez Boost możesz to zrobić ręcznie, albo w postaci typu obiektu generatorastd::generate
, albo jako coś, co możesz podłączyć do odręczny iterator liczący).źródło
std::vector
jest nazywany? Musi być konstruktorem zakresu , ale jestboost::counting_iterator
niejawnie konwertowany na InputIterator ?Widziałem odpowiedzi za pomocą std :: gene, ale można to również „poprawić”, używając zmiennych statycznych wewnątrz lambdy, zamiast deklarowania licznika poza funkcją lub tworzenia klasy generatora:
std::vector<int> vec; std::generate(vec.begin(), vec.end(), [] { static int i = 0; return i++; });
Uważam, że jest to trochę bardziej zwięzłe
źródło
static
ma tutaj niewłaściwą semantykę, IMO. Użyłbym uogólnionego przechwytywania[i = 0]() mutable
, aby było jasne, że zmienna jest ograniczona do konkretnego wystąpienia lambda, a nie do jej wygenerowanego typu klasy. Trudno jest wymyślić sytuację, w której byłaby różnica w praktyce, a to prawdopodobnie wskazywałoby na wątpliwy projekt, ale w każdym razie myślę, że semantyka jest lepsza przy użyciu zmiennej składowej. Poza tym tworzy bardziej zwięzły kod; teraz ciało lambdy może być pojedynczą instrukcją.Jeśli wolisz nie używać funkcji C ++ 11, możesz użyć
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; } }
Ten program drukuje od 0 do 9.
źródło
std::iota
tak masz , nie?it
iend
są zdefiniowane poza pętlą for. Jakiś powód?std::vector<int>::const_iterator it = vec.begin(), end = ivec.end()
(ani wielokrotne pisanie, ani powtarzające się dzwonienie)? A linia jest dłuższa, cóż, to C ++ i i tak nie da się obejść od czasu do czasu przerwania linii (a czasy dobrych starych 80-znakowych wyświetlaczy się skończyły). Ale myślę, że to kwestia gustu, a i tak dostałeś moją opinię dawno temu.Możemy użyć generatora funkcji która istnieje w pliku nagłówkowym algorytmu.
Fragment kodu:
#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; }
źródło
n
element lambda przez uogólnione przechwytywanie[n = 0]() mutable
, więc nie zanieczyszcza otaczającego zakresu.std :: iota jest ograniczone do sekwencji n, n + 1, n + 2, ...
Ale co, jeśli chcesz wypełnić tablicę generyczną sekwencją f (0), f (1), f (2) itd.? Często możemy uniknąć generatora śledzenia stanu. Na przykład,
int a[7]; auto f = [](int x) { return x*x; }; transform(a, a+7, a, [a, f](int &x) {return f(&x - a);});
stworzy sekwencję kwadratów
Jednak ta sztuczka nie zadziała z innymi kontenerami.
Jeśli utkniesz z C ++ 98, możesz robić okropne rzeczy, takie jak:
int f(int &x) { int y = (int) (long) &x / sizeof(int); return y*y; }
i wtedy
int a[7]; transform((int *) 0, ((int *) 0) + 7, a, f);
Ale nie polecałbym tego. :)
źródło
Mówiąc o wzmocnieniu:
auto ivec = boost::copy_range<std::vector<int>>(boost::irange(5, 10));
źródło
to też działa
j=0; for(std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it){ *it = j++; }
źródło
Pod względem wydajności należy zainicjalizować wektor za pomocą
reserve()
kombinacjipush_back()
funkcji jak w poniższym przykładzie:const int numberOfElements = 10; std::vector<int> data; data.reserve(numberOfElements); for(int i = 0; i < numberOfElements; i++) data.push_back(i);
Wszyscy
std::fill
,std::generate
itp działają w zakresie istniejącej zawartości wektorowych, a zatem wektor musi być wypełniona niektóre dane wcześniej. Nawet wykonując następujące czynności:std::vector<int> data(10);
tworzy wektor ze wszystkimi elementami ustawionymi na wartości domyślne (tj. 0 w przypadkuint
).Powyższy kod pozwala uniknąć inicjalizacji zawartości wektorowej przed wypełnieniem jej danymi, które naprawdę chcesz. Wydajność tego rozwiązania jest dobrze widoczna na dużych zbiorach danych.
źródło
Jest jeszcze jedna opcja - bez użycia joty. Można użyć wyrażenia lambda 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;});
Dwie ważne rzeczy, dlaczego to działa:
źródło
Jeśli naprawdę chcesz używać
std::fill
i jesteś ograniczony do C ++ 98, możesz użyć czegoś podobnego do następującego:#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; }
źródło
Wiem, że to stare pytanie, ale obecnie bawię się biblioteką, aby rozwiązać dokładnie ten problem. Wymaga 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, ...
źródło
_
są zastrzeżone dla implementacji tam.Stworzyłem prostą funkcję szablonową
Sequence()
do generowania ciągów liczb. Funkcjonalność jest zgodna zseq()
funkcją w R ( link ). Zaletą tej funkcji jest to, że działa ona w celu generowania różnych sekwencji liczb i typów.#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; }
Przykładowe użycie:
int main() { auto vec = Sequence(0., 10., 0.5); for(auto &v : vec) { std::cout << v << std::endl; } }
Jedynym zastrzeżeniem jest to, że wszystkie liczby powinny być tego samego wywnioskowanego typu. Innymi słowy, w przypadku liczb podwójnych lub zmiennoprzecinkowych uwzględnij ułamki dziesiętne dla wszystkich danych wejściowych, jak pokazano.
Zaktualizowano: 14 czerwca 2018 r
źródło
brainsandwich i underscore_d dały bardzo dobre pomysły. Ponieważ wypełnienie jest zmianą treści, for_each (), najprostszy z algorytmów STL, powinien również wypełnić rachunek:
std::vector<int> v(10); std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i++;});
Uogólnione przechwytywanie
[i=o]
przekazuje wyrażenie lambda niezmiennikiem i inicjuje je do znanego stanu (w tym przypadku 0). słowo kluczowemutable
umożliwia aktualizację tego stanu przy każdym wywołaniu lambda.Wystarczy niewielka modyfikacja, aby uzyskać sekwencję kwadratów:
std::vector<int> v(10); std::for_each(v.begin(), v.end(), [i=0] (int& x) mutable {x = i*i; i++;});
Generowanie sekwencji podobnej do R nie jest trudniejsze, ale zauważ, że w R tryb numeryczny jest w rzeczywistości podwójny, więc naprawdę nie ma potrzeby parametryzowania typu. Po prostu użyj podwójnego.
źródło