I have a pattern in which I want to fill an array:
even indices value = -1
odd indices value = 1
I currently achieve it like this:
#include <array>
#include <algorithm>
using namespace std;
int generator(){
static int i = 1;
i *= -1;
return i;
}
std::array<int ,64> arr;
std::generate(arr.begin(), arr.end(), generator);
Edit: Current implementation has a caveat - returned value of generator
doesn't depend on iteration's object index.
Is there a way to pass current index to the generator
function so it's output will depend on this index?
Your generator
function has a static
local variable, which preserves its state across every call. This means that if you use generator
in a different call it remembers its old state, which might not be what you want.
You can fix this by writing a function that gives you a "generator" every time you call it. Note that the returned value (ie the generator itself) is just a mutable
lambda, so that the value of i
is preserved across multiple calls to that lambda.
auto gen()
{
return [i = -1]() mutable
{
i *= -1;
return i;
};
}
// usage ...
std::generate(arr.begin(), arr.end(), gen());
Here's a demo .
As @cigen has pointed out, your code works as written.
But to try to answer your question, "Is there a way to pass current index to the generator function?" - the answer is "no, not with std::generate
". Here's a sample implementation of that call (taken from CppReference :
template<class ForwardIt, class Generator>
void generate(ForwardIt first, ForwardIt last, Generator g)
{
while (first != last) {
*first++ = g();
}
}
Points to note:
generate
does not use indexes, so it can't pass them to the generator. All of the solutions assume your generator will be called sequentially, what about parallel and out-of-sequence execution?
std::array<int, 64> arr;
std::iota(arr.begin(), arr.end(), 0);
std::transform(arr.begin(), arr.end(), arr.begin(), [](auto const i){return i % 2: -1 : 1;});
You want to use the wrong function. If the element index is an argument to your generator, then std::transform()
fits your requirements better.
EDIT: You need c++20 for an authoritative solution:
std::array<int, 64> arr;
auto const g(std::views::iota(decltype(arr)::size_type{}, arr.size()));
std::transform(g.begin(), g.end(), arr.begin(), [](auto const i){return i % 2 ? -1 : 1;});
or:
std::array<int, 64> arr;
std::ranges::copy(std::views::iota(decltype(arr)::size_type{}, arr.size()) | std::views::transform([](auto const i){return i % 2 ? -1 : 1;}), arr.begin());
You can run this in parallel and/or out-of-sequence and there may be other solutions as well.
If you want to attach state to a function it is better to use a function object rather than function local static variables (for the very reason you discovered: reusability).
struct generator2 {
bool even = true;
int operator()(){
even = !even;
return even ? -1 : 1;
}
};
int main() {
std::array<int ,64> arr;
std::generate(arr.begin(), arr.end(), generator2{});
for (auto e : arr) std::cout << e;
}
What is passed to std::generate
here is an instance of type generator2
whose operator()
will be called.
Is there a way to pass current index to the generator function?
No. The generator for std::generate
must be callable without parameter:
The signature of the function should be equivalent to the following:
Ret fun();
The type Ret must be such that an object of type ForwardIt can be dereferenced and assigned a value of type Ret.
Just because it's hard to do with std::generate
doesn't mean it can't be done. You can write your own version of generate
:
namespace mystl {
template<class ForwardIt, class Generator>
void generate(ForwardIt first, ForwardIt last, Generator g)
{
int idx = 0;
while (first != last) {
*first++ = g(idx++);
}
}
}
Don't be limited by the standard algorithms; there's nothing magical about most of the ones in the standard library. Don't be afraid to write your own!
You cannot pass current index to the generator
function with std::generate
. But you can use a lambda to do that:
std::array<int ,64> arr;
auto generator = [i = 1, n = 0] () mutable {
// n is the current index here
i = -i;
n++;
return i;
};
std::generate(arr.begin(), arr.end(), generator);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.