The C++ Standard Library provides several algorithms for assigning values to a range (such as a container or a C-array).
fill and fill_n
std::fill assigns a given value to all the elements in a range. It is available in the header <algorithm>. In the following example all elements of a vector of integers are assigned the value 1.
std::vectordata(10); std::fill(std::begin(data), std::end(data), 1);
std::fill_n is similar, but instead taking the bounds of a range it takes the beginning and a count and initializes N elements starting from the specified first element. You must make sure that first + count does not exceed the bounds of the range. The following example is an equivalent of the first:
std::vectordata(10); std::fill_n(std::begin(data), data.size(), 1);
generate and generate_n
These algorithms are similar to the first, but instead of taking a value to assign they take a function and assign the value returned by the function. They are also available in the header <algorithm>.
std::generate assigns the elements in a range the values returned by a function.
In the following example we initialize a vector of 10 elements with random numbers (using a Marsenne twister engine).
std::mt19937 rnd(static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())); std::vector<int> data(10); std::generate(std::begin(data), std::end(data), rnd); for(auto const & d : data) { std::cout << d << std::endl; }
The result may look like this:
-630855823 -1828303634 473207879 1464137724 1046536659 1518951902 1516051318 -2076855687 -1620034611 -991968006
You can use lambdas as the function whose return value is stored into the range. The following example generates random numbers ranging from 1 to 100 with a uniform distribution.
std::mt19937 rnd(static_cast(std::chrono::system_clock::now().time_since_epoch().count())); std::vector data(10); std::uniform_int_distribution<> ud(1, 100); std::generate(std::begin(data), std::end(data), [&](){return ud(rnd);}); for(auto const & d : data) { std::cout << d << std::endl; }
Result may look like this:
48 10 15 63 34 2 22 71 15 37
std::generate_n is similar, but instead of taking the limits of the range it takes the beginning and a count and assigns the first N values returned by the function to the range. You must makes sure that first + count does not exceeds the bounds of the range.
In the following example we initialize the elements of a vector with values from 1 to N, where N is the size of the range.
std::vectordata(10); int n = 1; std::generate_n(std::begin(data), data.size(), [&](){return n++;}); for(auto const & d : data) { std::cout << d << std::endl; }
The result is:
1 2 3 4 5 6 7 8 9 10
iota
std::iota is a new algorithm to C++11. It assigns the elements of a range with sequentially increasing values starting from a given value. The algorithm is available in header <numeric>.
The following example initializes the elements of a vector with values from 1 to N, where N is the size of the range (basically the same thing we did in the last example).
std::vectordata(10); std::iota(std::begin(data), std::end(data), 1); for(auto const & d : data) { std::cout << d << std::endl; }
The result is:
1 2 3 4 5 6 7 8 9 10
Notice that the term iota is taken from the APL programming language and denotes the Greek letter ⍳ used in various mathematical notations.
Minor nit: generate (and generate_n) does not take a predicate – a function that takes a value and returns true or false.
Good catch. Thanks for the heads up.
Thank you for the informative post, Marius.
I always forget about the numeric header and what’s contained within it. :p