C++ How to use lambda expression to capture the previous iteration value? - c++

I'm using this simple example of creating a simple vector with its element in strictly increasing, random step manner. I have an implementation using function objects:
struct IncrementGenerator
{
int m;
IncrementGenerator() : m(0) {}
int operator()()
{
m += 1 + (rand() % 5); //random Increment between [1,5]
return m;
}
};
vector<int> myVector(10);
generate(myVector.begin()+1, myVector.end(), IncrementGenerator());
//example of the output: 0, 4, 6, 9, 10, 14, 15, 17, 20, 25,
I've just learnt lambda expression today and would like to do the same implementation in much short code. I'm not sure if I can capture the previous iteration in the lambda expression and add on a random number onto it:
// my idea is such, but not really sure what to write in the expression so i put some question mark.
generate(myVector.begin()+1, myVector.end(),[?]()->int{return ? + (1 + (rand()%5)) });

The lambda equivalent to the class above is this:
auto increment_generator = [m = 0]() mutable
{
m += 1 + (rand() % 5); //random Increment between [1,5]
return m;
};
Note the mutable modifier which indicates that the captured m is not constant. By default, variables captured by lambdas are const.

Related

Is it possible to make a vector of ranges in cpp20

Let's say I have a a vector<vector<int>>. I want to use ranges::transform in such a way that I get
vector<vector<int>> original_vectors;
using T = decltype(ranges::views::transform(original_vectors[0], [&](int x){
return x;
}));
vector<int> transformation_coeff;
vector<T> transformed_vectors;
for(int i=0;i<n;i++){
transformed_vectors.push_back(ranges::views::transform(original_vectors[i], [&](int x){
return x * transformation_coeff[i];
}));
}
Is such a transformation, or something similar, currently possible in C++?
I know its possible to simply store the transformation_coeff, but it's inconvenient to apply it at every step. (This will be repeated multiple times so it needs to be done in O(log n), therefore I can't explicitly apply the transformation).
Yes, you can have a vector of ranges. The problem in your code is that you are using a temporary lambda in your using statement. Because of that, the type of the item you are pushing into the vector later is different from T. You can solve it by assigning the lambda to a variable first:
vector<vector<int>> original_vectors;
auto lambda = [&](int x){return x;};
using T = decltype(ranges::views::transform(original_vectors[0], lambda));
vector<T> transformed_vectors;
transformed_vectors.push_back(ranges::views::transform(original_vectors[0], lambda));
It is not possible in general to store different ranges in a homogeneous collection like std::vector, because different ranges usually have different types, especially if transforms using lambdas are involved. No two lambdas have the same type and the type of the lambda will be part of the range type. If the signatures of the functions you want to pass to the transform are the same, you could wrap the lambdas in std::function as suggested by #IlCapitano (https://godbolt.org/z/zGETzG4xW). Note that this comes at the cost of the additional overhead std::function entails.
A better option might be to create a range of ranges.
If I understand you correctly, you have a vector of n vectors, e.g.
std::vector<std::vector<int>> original_vector = {
{1, 5, 10},
{2, 4, 8},
{5, 10, 15}
};
and a vector of n coefficients, e.g.
std::vector<int> transformation_coeff = {2, 1, 3};
and you want a range of ranges representing the transformed vectors, where the ith range represents the ith vector's elements which have been multiplied by the ith coefficient:
{
{ 2, 10, 20}, // {1, 5, 10} * 2
{ 2, 4, 8}, // {2, 4, 8} * 1
{15, 30, 45} // {5, 10, 15} * 3
}
Did I understand you correctly? If yes, I don't understand what you mean with your complexity requirement of O(log n). What does n refer to in this scenario? How would this calculation be possible in less than n steps? Here is a solution that gives you the range of ranges you want. Evaluating this range requires O(n*m) multiplications, where m is an upper bound for the number of elements in each inner vector. I don't think it can be done in less steps because you have to multiply each element in original_vector once. Of course, you can always just evaluate part of the range, because the evaluation is lazy.
C++20
The strategy is to first create a range for the transformed i-th vector given the index i. Then you can create a range of ints using std::views::iota and transform it to the inner ranges:
auto transformed_ranges = std::views::iota(0) | std::views::transform(
[=](int i){
// get a range containing only the ith inner range
auto ith = original_vector | std::views::drop(i) | std::views::take(1) | std::views::join;
// transform the ith inner range
return ith | std::views::transform(
[=](auto const& x){
return x * transformation_coeff[i];
}
);
}
);
You can now do
for (auto const& transformed_range : transformed_ranges){
for (auto const& val : transformed_range){
std::cout << val << " ";
}
std::cout<<"\n";
}
Output:
2 10 20
2 4 8
15 30 45
Full Code on Godbolt Compiler Explorer
C++23
This is the perfect job for C++23's std::views::zip_transform:
auto transformed_ranges = std::views::zip_transform(
[=](auto const& ith, auto const& coeff){
return ith | std::views::transform(
[=](auto const& x){
return x * coeff;
}
);
},
original_vector,
transformation_coeff
);
It's a bit shorter and has the added benefit that transformation_coeff is treated as a range as well:
It is more general, because we are not restricted to std::vectors
In the C++20 solution you get undefined behaviour without additional size checking if transformation_coeff.size() < original_vector.size() because we are indexing into the vector, while the C++23 solution would just return a range with fewer elements.
Full Code on Godbold Compiler Explorer

Can you access the current iterator from a function used by the transform function in c++?

Can you access the current iterator from a function used by the transform function in c++ so that you can reference previous and latter values?
I want to use the transform function to iterate through a vector, performing operations on the vector that rely on values before and after the current value.
For example, say I have a vector with values [1,2,4,3,5,6], and I want to start at the second value, and iterate until the second to last value. On each of those elements, I want to make a new value that equals the sum of the value, and the values next to it in the original.
The ending vector would look like
[7,9,12,14].
auto originalsBeginIterator = originalPoints.begin();
auto originalsEndIterator = originalPoints.end();
std::advance(originalsBeginIterator, 1);
std::advance(originalsEndIterator,-1);
std::transform(originalsBeginIterator,originalsEndIterator,alteredValues.begin(),
[](int x) x = { return {previous value} + x + {next value};}
);
Is there any way to reference previous and latter values from the original array when using transform?
Clearly the tool std::transform simply doesn't give you a way to do that: it either takes a unary predicate to be applied to individual elements of of one collection, or a binary predicate to be applied to corresponding elements of two collections.
But the point is that, from the functional programming perspective, what you are trying to do is simply not a transform.
How can you go about it instead? You could zip that vector, let's call it v, the same vector deprived of its first element, and the same vector deprived from its second element; you would then sum the 3 elements of each pair.
Range-v3 gives you a way to do this quite tersely:
#include <iostream>
#include <range/v3/view/drop.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/zip_with.hpp>
#include <vector>
using namespace ranges::views;
int main()
{
// input
std::vector<int> v{1,2,3,4,5,6};
// to sum 3 ints
auto constexpr plus = [](int x, int y, int z){ return x + y + z; };
// one-liner
auto w = zip_with(plus, v, v | drop(1), v | drop(2));
// output
std::cout << w << std::endl;
}
v | drop(1) gives you a view on the elements {2,3,4,5,6}, and v | drop(2) on {3,4,5,6}; zip_with taks a n-ary function and n ranges and combines the n-tuple of corresponding elements from the n ranges using the n-ary function. So in our case it'll go like this:
v = {1, 2, 3, 4, 5, 6}
+ + + +
v1 = v | drop(1) = {2, 3, 4, 5, 6}
+ + + +
v2 = v | drop(2) = {3, 4, 5, 6}
zip_with(plus, v, v1, v2) = {6, 9,12,15}

How can I improve the following lambda function for finding the first 4 elements in a vector

For practice, I am trying to copy the first 4 entries different than 2 from a vector of integers using copy_if.
This seems to work but if there is a better way of writing this lambda then I'd like to learn the proper way. Cheers.
vector<int> first_vector = {2,8,50,2,4,5,9,12};
vector<int> second_vector (first_vector.size());
int count_elem=0;
auto it = copy_if(first_vector.begin(),first_vector.end(),second_vector.begin(),
[&count_elem]
(int i){
if(i!=2 && count_elem!=4)
{
count_elem++;
return 1;
}
return 0;});
Since you are not copying all of the values from first_vector to second_vector, you should not initialize second_vector to hold the same number of elements as first_vector. You are creating more elements than you want, where the extra elements are value-initialized to 0.
I would suggest reserve()'ing the size of second_vector instead and then use std::back_inserter as the destination iterator to copy to. That way, second_vector ends up with only the values you want pushed and nothing else.
That would also eliminate the need for the count_elem variable. You can use second_vector.size() to know how many values have been pushed into the vector.
std::vector<int> first_vector = {2, 8, 50, 2, 4, 5, 9, 12};
std::vector<int> second_vector;
second_vector.reserve(4);
std::copy_if(
first_vector.begin(), first_vector.end(),
std::back_inserter(second_vector),
[&](int i){
return ((i != 2) && (second_vector.size() < 4));
}
);
Do note, however, that this use of std::copy_if() will iterate through the entire first_vector, it will not stop iterating once 4 values have been pushed to second_vector. It would be more efficient to simply run your own loop instead so you can break it as soon as possible:
std::vector<int> first_vector = {2, 8, 50, 2, 4, 5, 9, 12};
std::vector<int> second_vector;
second_vector.reserve(4);
for(int i : first_vector) {
if (i != 2) {
second_vector.push_back(i);
if (second_vector.size() == 4)
break;
}
}

Slice view for C++20 ranges

Python's itertools has the islice(seq, start, stop, step) procedure that takes a sequence and returns an iterator of every stepth value of the sequence values between start and stop.
Does the Ranges library of C++20 provide a similar functionality, e.g. a function like slice that takes a random access iterator start, a sentinel stop, and a step value step, and that returns a random access iterator that iterates over every stepth value between start and stop?
In case it does not, can such an iterator adapter be implemented using the primitives provided by the Ranges library?
(I know how I can implement such an adapter by hand, so this is not the question.)
Not quite.
C++20 will have view::iota which gives you a sequence from a starting value to a sentinel. However, it does not have the stride functionality. It only increments (via ++).
However, you can combine it with range-v3's view::stride to add in the steps. That is:
auto evens = view::iota(0, 100) | view::stride(2); // [0, 2, 4, 6, ... ]
For existing ranges, there's view::slice, which also doesn't take a stride. But these are orthogonal and layer nicely:
auto even_teens = view::iota(0, 100)
| view::slice(10, 20)
| view::stride(2); // [10, 12, 14, 16, 18]
Unfortunaltely, slice and stride of Range-v3, as presented in Barry'sanswer, are not (yet) available in the Ranges library of C++20.
However, you can replace slice by combining std::views::drop_while and std::views::take_while. To replace stride, you can use the range adaptor std::views::filter and pass a specific lambda expression to it. To filter for every other element as in Barry's example, I would use a stateful lambda expression with an init capture. You can put everything together to represent the range [10, 12, 14, 16, 18] as follows:
auto even_teens = std::views::iota(0, 100)
| std::views::drop_while([](int i) { return i < 10; })
| std::views::take_while([](int i) { return i < 20; })
| std::views::filter([s = false](auto const&) mutable { return s = !s; });
For a more universal stride solution, you can use a counter along with the modulo operator in the lambda expression. To be able to specify the stride size n in a readable way, I would use the following lambda expression, which provides another lambda expression that keeps track of the stride operation:
auto stride = [](int n) {
return [s = -1, n](auto const&) mutable { s = (s + 1) % n; return !s; };
};
All in all, the final solution looks like this:
auto even_teens = std::views::iota(0, 100)
| std::views::drop_while([](int i) { return i < 10; })
| std::views::take_while([](int i) { return i < 20; })
| std::views::filter(stride(2));
Code on Wandbox

How to produce a random number sequence that doesn't produce more than X consecutive elements

Ok, I really don't know how to frame the question properly because I barely have any idea how to describe what I want in one sentence and I apologize.
Let me get straight to the point and you can just skip the rest cause I just want to show that I've tried something and not coming here to ask a question on a whim.
I need an algorithm that produces 6 random numbers where it may not produce more than 2 consecutive numbers in that sequence.
example: 3 3 4 4 2 1
^FINE.
example: 3 3 3 4 4 2
^NO! NO! WRONG!
Obviously, I have no idea how to do this without tripping over myself constantly.
Is there a STL or Boost feature that can do this? Or maybe someone here knows how to concoct an algorithm for it. That would be awesome.
What I'm trying to do and what I've tried.(the part you can skip)
This is in C++. I'm trying to make a Panel de Pon/Tetris Attack/Puzzle League whatever clone for practice. The game has a 6 block row and 3 or more matching blocks will destroy the blocks. Here's a video in case you're not familiar.
When a new row comes from the bottom it must not come out with 3 horizontal matching blocks or else it will automatically disappear. Something I do not want for horizontal. Vertical is fine though.
I've tried to accomplish just that and it appears I can't get it right. When I start the game chunks of blocks are missing because it detects a match when it shouldn't. My method is more than likely heavy handed and too convoluted as you'll see.
enum BlockType {EMPTY, STAR, UP_TRIANGLE, DOWN_TRIANGLE, CIRCLE, HEART, DIAMOND};
vector<Block> BlockField::ConstructRow()
{
vector<Block> row;
int type = (rand() % 6)+1;
for (int i=0;i<6;i++)
{
row.push_back(Block(type));
type = (rand() % 6) +1;
}
// must be in order from last to first of the enumeration
RowCheck(row, diamond_match);
RowCheck(row, heart_match);
RowCheck(row, circle_match);
RowCheck(row, downtriangle_match);
RowCheck(row, uptriangle_match);
RowCheck(row, star_match);
return row;
}
void BlockField::RowCheck(vector<Block> &row, Block blockCheckArray[3])
{
vector<Block>::iterator block1 = row.begin();
vector<Block>::iterator block2 = row.begin()+1;
vector<Block>::iterator block3 = row.begin()+2;
vector<Block>::iterator block4 = row.begin()+3;
vector<Block>::iterator block5 = row.begin()+4;
vector<Block>::iterator block6 = row.begin()+5;
int bt1 = (*block1).BlockType();
int bt2 = (*block2).BlockType();
int bt3 = (*block3).BlockType();
int bt4 = (*block4).BlockType();
int type = 0;
if (equal(block1, block4, blockCheckArray))
{
type = bt1 - 1;
if (type <= 0) type = 6;
(*block1).AssignBlockType(type);
}
else if (equal(block2, block5, blockCheckArray))
{
type = bt2 - 1;
if (type <= 0) type = 6;
(*block2).AssignBlockType(type);
}
else if (equal(block3, block6, blockCheckArray))
{
type = bt3 - 1;
if (type == bt3) type--;
if (type <= 0) type = 6;
(*block3).AssignBlockType(type);
}
else if (equal(block4, row.end(), blockCheckArray))
{
type = bt4 - 1;
if (type == bt3) type--;
if (type <= 0) type = 6;
(*block4).AssignBlockType(type);
}
}
Sigh, I'm not sure if it helps to show this...At least it shows that I've tried something.
Basically, I construct the row by assigning random block types, described by the BlockType enum, to a Block object's constructor(a Block object has blockType and a position).
Then I use a RowCheck function to see if there's 3 consecutive blockTypes in one row and I have do this for all block types. The *_match variables are arrays of 3 Block objects with the same block type. If I do find that there are 3 consecutive block types then, I just simply subtract the first value by one. However if I do that I might end up inadvertently producing another 3 match so I just make sure the block types are going in order from greatest to least.
Ok, it's crappy, it's convoluted and it doesn't work! That's why I need your help.
It should suffice to keep record of the previous two values, and loop when the newly generated one matches both of the previous values.
For an arbitrary run length, it would make sense to size a history buffer on the fly and do the comparisons in a loop as well. But this should be close to matching your requirements.
int type, type_old, type_older;
type_older = (rand() % 6)+1;
row.push_back(Block(type_older));
type_old = (rand() % 6)+1;
row.push_back(Block(type_old));
for (int i=2; i<6; i++)
{
type = (rand() % 6) +1;
while ((type == type_old) && (type == type_older)) {
type = (rand() % 6) +1;
}
row.push_back(Block(type));
type_older = type_old;
type_old = type;
}
Idea no 1.
while(sequence doesn't satisfy you)
generate a new sequence
Idea no 2.
Precalculate all allowable sequences (there are about ~250K of them)
randomly choose an index and take that element.
The second idea requires much memory, but is fast. The first one isn't slow either because there is a veeery little probability that your while loop will iterate more than once or twice. HTH
Most solutions seen so far involve a potentially infinite loop. May I suggest a different approch?
// generates a random number between 1 and 6
// but never the same number three times in a row
int dice()
{
static int a = -2;
static int b = -1;
int c;
if (a != b)
{
// last two were different, pick any of the 6 numbers
c = rand() % 6 + 1;
}
else
{
// last two were equal, so we need to choose from 5 numbers only
c = rand() % 5;
// prevent the same number from being generated again
if (c == b) c = 6;
}
a = b;
b = c;
return c;
}
The interesting part is the else block. If the last two numbers were equal, there is only 5 different numbers to choose from, so I use rand() % 5 instead of rand() % 6. This call could still produce the same number, and it also cannot produce the 6, so I simply map that number to 6.
Solution with simple do-while loop (good enough for most cases):
vector<Block> row;
int type = (rand() % 6) + 1, new_type;
int repetition = 0;
for (int i = 0; i < 6; i++)
{
row.push_back(Block(type));
do {
new_type = (rand() % 6) + 1;
} while (repetition == MAX_REPETITION && new_type == type);
repetition = new_type == type ? repetition + 1 : 0;
type = new_type;
}
Solution without loop (for those who dislike non-deterministic nature of previous solution):
vector<Block> row;
int type = (rand() % 6) + 1, new_type;
int repetition = 0;
for (int i = 0; i < 6; i++)
{
row.push_back(Block(type));
if (repetition != MAX_REPETITION)
new_type = (rand() % 6) + 1;
else
{
new_type = (rand() % 5) + 1;
if (new_type >= type)
new_type++;
}
repetition = new_type == type ? repetition + 1 : 0;
type = new_type;
}
In both solutions MAX_REPETITION is equal to 1 for your case.
How about initializing a six element array to [1, 2, 3, 4, 5, 6] and randomly interchanging them for awhile? That is guaranteed to have no duplicates.
Lots of answers say "once you detect Xs in a row, recalculate the last one until you don't get an X".... In practice for a game like this, that approach is millions of times faster than you need for "real-time" human interaction, so just do it!
But, you're obviously uncomfortable with it and looking for something more inherently "bounded" and elegant. So, given you're generating numbers from 1..6, when you detect 2 Xs you already know the next one could be a duplicate, so there are only 5 valid values: generate a random number from 1 to 5, and if it's >= X, increment it by one more.
That works a bit like this:
1..6 -> 3
1..6 -> 3
"oh no, we've got two 3s in a row"
1..5 -> ?
< "X"/3 i.e. 1, 2 use as is
>= "X" 3, 4, 5, add 1 to produce 4, 5 or 6.
Then you know the last two elements differ... the latter would take up the first spot when you resume checking for 2 elements in a row....
vector<BlockType> constructRow()
{
vector<BlockType> row;
row.push_back(STAR); row.push_back(STAR);
row.push_back(UP_TRIANGLE); row.push_back(UP_TRIANGLE);
row.push_back(DOWN_TRIANGLE); row.push_back(DOWN_TRIANGLE);
row.push_back(CIRCLE); row.push_back(CIRCLE);
row.push_back(HEART); row.push_back(HEART);
row.push_back(DIAMOND); row.push_back(DIAMOND);
do
{
random_shuffle(row.begin(), row.end());
}while(rowCheckFails(row));
return row;
}
The idea is to use random_shuffle() here. You need to implement rowCheckFails() that satisfies the requirement.
EDIT
I may not understand your requirement properly. That's why I've put 2 of each block type in the row. You may need to put more.
I think you would be better served to hide your random number generation behind a method or function. It could be a method or function that returns three random numbers at once, making sure that there are at least two distinct numbers in your output. It could also be a stream generator that makes sure that it never outputs three identical numbers in a row.
int[] get_random() {
int[] ret;
ret[0] = rand() % 6 + 1;
ret[1] = rand() % 6 + 1;
ret[2] = rand() % 6 + 1;
if (ret[0] == ret[1] && ret[1] == ret[2]) {
int replacement;
do {
replacement = rand() % 6 + 1;
} while (replacement == ret[0]);
ret[rand() % 3] = replacement;
}
return ret;
}
If you wanted six random numbers (it's a little difficult for me to tell, and the video was just baffling :) then it'll be a little more effort to generate the if condition:
for (int i=0; i<4; i++) {
if (ret[i] == ret[i+1] && ret[i+1] == ret[i+2])
/* three in a row */
If you always change ret[1] (the middle of the three) you'll never have three-in-a-row as a result of the change, but the output won't be random either: X Y X will happen more often than X X Y because it can happen by random chance and by being forced in the event of X X X.
First some comments on the above solutions.
There is nothing wrong with the techniques that involve rejecting a random value if it isn't satisfactory. This is an example of rejection sampling, a widely used technique. For example, several algorithms for generating a random gaussian involve rejection sampling. One, the polar rejection method, involves repeatedly drawing a pair of numbers from U(-1,1) until both are non-zero and do not lie outside the unit circle. This throws out over 21% of the pairs. After finding a satisfactory pair, a simple transformation yields a pair of gaussian deviates. (The polar rejection method is now falling out of favor, being replaced by the ziggurat algorithm. That too uses a rejection sampling.)
There is something very much wrong with rand() % 6. Don't do this. Ever. The low order bits from a random number generator, even a good random number generator, are not quite as "random" as are the high order bits.
There is something very much wrong with rand(), period. Most compiler writers apparently don't know beans about producing random numbers. Don't use rand().
Now a solution that uses the Boost random number library:
vector<Block> BlockField::ConstructRow(
unsigned int max_run) // Maximum number of consecutive duplicates allowed
{
// The Mersenne Twister produces high quality random numbers ...
boost::mt19937 rng;
// ... but we want numbers between 1 and 6 ...
boost::uniform_int<> six(1,6);
// ... so we need to glue the rng to our desired output.
boost::variate_generator<boost::mt19937&, boost::uniform_int<> >
roll_die(rng, six);
vector<Block> row;
int prev = 0;
int run_length = 0;
for (int ii=0; ii<6; ++ii) {
int next;
do {
next = roll_die();
run_length = (next == prev) ? run_length+1 : 0;
} while (run_length > max_run);
row.push_back(Block(next));
prev = next;
}
return row;
}
I know that this already has many answers, but a thought just occurred to me. You could have 7 arrays, one with all 6 digits, and one for each missing a given digit. Like this:
int v[7][6] = {
{1, 2, 3, 4, 5, 6 },
{2, 3, 4, 5, 6, 0 }, // zeros in here to make the code simpler,
{1, 3, 4, 5, 6, 0 }, // they are never used
{1, 2, 4, 5, 6, 0 },
{1, 2, 3, 5, 6, 0 },
{1, 2, 3, 4, 6, 0 },
{1, 2, 3, 4, 5, 0 }
};
Then you can have a 2 level history. Finally to generate a number, if your match history is less than the max, shuffle v[0] and take v[0][0]. Otherwise, shuffle the first 5 values from v[n] and take v[n][0]. Something like this:
#include <algorithm>
int generate() {
static int prev = -1;
static int repeat_count = 1;
static int v[7][6] = {
{1, 2, 3, 4, 5, 6 },
{2, 3, 4, 5, 6, 0 }, // zeros in here to make the code simpler,
{1, 3, 4, 5, 6, 0 }, // they are never used
{1, 2, 4, 5, 6, 0 },
{1, 2, 3, 5, 6, 0 },
{1, 2, 3, 4, 6, 0 },
{1, 2, 3, 4, 5, 0 }
};
int r;
if(repeat_count < 2) {
std::random_shuffle(v[0], v[0] + 6);
r = v[0][0];
} else {
std::random_shuffle(v[prev], v[prev] + 5);
r = v[prev][0];
}
if(r == prev) {
++repeat_count;
} else {
repeat_count = 1;
}
prev = r;
return r;
}
This should result in good randomness (not reliant of rand() % N), no infinite loops, and should be fairly efficient given the small amount of numbers that we are shuffling each time.
Note, due to the use of statics, this is not thread safe, that may be fine for your usages, if it is not, then you probably want to wrap this up in an object, each with its own state.