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
Related
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
Is it possible to have a for loop that starts at the middle of a range and then loops around to the beginning of the range and continues until it loops through the whole range? For example, assuming we have a range of 0 to 63, I want the loop to start at 22, continue looping until it reaches 63, then start at 0 and loop until it reaches 21. Is this possible with a for loop? Or would I require a while loop?
I'd use two loop variables: one to count the number of repetitions and one to handle the desired index. Like this:
for (int i = 0, j = 22; i < 64; ++i, j = (j + 1) % 64)
// do something with j
Of course, in real code you'd replace the magic numbers (22, 64) with something that more clearly reflects the actual objects involved.
Yes, you can use a range-based for loop for this. A C++20 STL example (with using namespace std::ranges):
constexpr void foo(auto&& range) {
auto middle = range.begin() + 22;
auto parts = {subrange{middle, range.end()}, subrange{range.begin(), middle}};
for (auto&& elem: parts | views::join) bar(elem);
}
Maybe there's a better looking way / library. With a proper API, it should be something like
constexpr void foo(auto&& range) {
for (auto&& elem: range | drop(22) | chain(range | take(22))) bar(elem);
}
Edit: almost the same "proper API" can be found ready in ranges::v3::view (concat here is an "unpipable" version of my imaginary chain):
constexpr void foo(auto&& range) {
for (auto&& elem: concat(range | drop(22), range | take(22))) bar(elem);
}
Yes, for example:
for( int i = 22, max = 63, end = i-1 ; i!=-1 ; i=i==end?-1:i==max?0:i+1 )
{
// do something with i
}
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.
I face a common problem in my code where I would like to remove only a single element from a reversed std::vector after it satisfies a predicate. I understand there's a number of ways to do this with ranges-v3, but each way I come up with seems a bit convoluted.
Here's an example of the target vector v:
std::vector v = { 1, 2, 3, 2, 4 };
The result needs to be vector r:
std::vector r = { 1, 2, 3, 4 };
Which will be done by removing the first 2 (via a lambda predicate "is_two") that is found when reverse traversing the vector v.
Here's one what it could look like in a vanilla C++ raw loop:
auto is_two = [](int a) { return a == 2; };
for (int i = v.size(); --i >= 0;) {
if (is_two(v[i])) {
v.erase(v.begin() + i);
break;
}
}
Here's my bad ranges-v3 version:
namespace rs = ranges;
namespace rv = ranges::view;
namespace ra = ranges::action;
rs::for_each(v | rv::enumerate
| rv::reverse
| rv::filter([](auto i_e) { return i_e.second == 2; })
| rv::take(1),
[&](auto& i_e) { v.erase(v.begin() + i_e.first); });
Ideally I'm wondering if there's some solution that could look something like this:
ra::remove_if(v | rv::reverse, is_two);
To generalize, I'd like to know how one can take a container, pipe it through some ranges::view operations, then remove the elements in the resulting range from the original container.
Since no one seems to have come up with a better approach,
I would like to mention for your benefit the possibility of resorting to good old reverse_iterators.
vec.erase(std::prev(ranges::find_if(vec.rbegin(), vec.rend(), is_two).base()));
Admittedly, this is not very ranges-ish,
but least it works.
The main purpose of range-based for loops is consistency. Having the same operation executed for each element.
Deleting an element breaks this consistency. So the best solution is a normal for loop where you can use iterators and breaks.
When you have a hammer everything looks like a nail. Don't hammer the ranged loop into a normal one.
Let say I have some values in a List. I would like to return another list with a new element
fun newList():List<Int>{
val values =listOf<Int>(1,2,3,4,5,6);
return 7::values; // something like that
}
The Kotlin lists have the plus operator overloaded in kotlin-stdlib, so you can add an item to a list:
val values = listOf(1, 2, 3, 4, 5, 6)
return values + 7
There's also an overload that adds another list:
val values = listOf(1, 2, 3, 4, 5, 6)
return listOf(-1, 0) + values + listOf(7, 8)
Note that in both cases a new list is created, and the elements are copied into it.
For MutableList<T> (which has mutating functions, in contrast with List<T>), there is a plusAssign operator implementation, that can be used as follows:
fun newList(): List<Int> {
val values = mutableListOf(1, 2, 3, 4, 5, 6)
values += 7
values += listOf(8, 9)
return values
}
A different approach by using spread operator. Maybe in some case would be simpler than using + sign or mutableListOf
fun newList(): List<Int>{
val values = listOf(1,2,3,4,5,6)
return listOf(7, *values.toTypedArray(), 8, 9)
}
You can do it like this
fun newList():List<Int>{
val values =listOf(1,2,3,4,5,6) //Type can be inferred
return values.plus(7)
}
I wanted a Scala-like with for and yield. It's pretty good in - currently experimental - coroutines :
fun testYield(max:Int): List<Int> {
val values = buildSequence{
for (i in 1..max){
yield(i)
}
}
return values.toList();
}
Or in shorter way:
fun testYieldFast(max: Int) = buildSequence {
for (i in 1..max)
yield(i)
}.toList();
It allows fast immutable lazy construction, where frequent concatenation are usually slow with immutable lists.