Lazy Evaluation

Understand the use of 'std::views::iota' with examples.

We'll cover the following...

std::views::iota is a range factory for creating a sequence of elements by successively incrementing an initial value. This sequence can be finite or infinite.

Filling vector with views::iota

The following program fills a std::vector with 1010 int’s, starting with 00.

C++
#include <iostream>
#include <numeric>
#include <ranges>
#include <vector>
int main() {
std::cout << std::boolalpha;
std::vector<int> vec;
std::vector<int> vec2;
for (int i: std::views::iota(0, 10)) vec.push_back(i);
for (int i: std::views::iota(0) | std::views::take(10)) vec2.push_back(i);
std::cout << "vec == vec2: " << (vec == vec2) << '\n';
for (int i: vec) std::cout << i << " ";
}

The first iota call (line 12) creates all numbers from 00 to 99, incremented by 11. The second iota call (line 14) creates an infinite data stream, starting with 00, incremented by 11. std::views::iota(0) is lazy. I only get a new value if I ask for it. I ask for it ten times. Consequently, both vectors are identical.

Another example

Now, I want to solve a small challenge: finding the first 2020 prime numbers starting with 1,000,0001,000,000.

C++
#include <iostream>
#include <ranges>
bool isPrime(int i) {
for (int j=2; j*j <= i; ++j) {
if (i % j == 0) return false;
}
return true;
}
int main() {
std::cout << "Numbers from 1'000'000 to 1'001'000 (displayed each 100th): "
<< '\n';
for (int i: std::views::iota(1'000'000, 1'001'000)) {
if (i % 100 == 0) std::cout << i << " ";
}
std::cout << "\n\n";
auto odd = [](int i){ return i % 2 == 1; };
std::cout << "Odd numbers from 1'000'000 to 1'001'000 (displayed each 100th): "
<< '\n';
for (int i: std::views::iota(1'000'000, 1'001'000) | std::views::filter(odd)) {
if (i % 100 == 1) std::cout << i << " ";
}
std::cout << "\n\n";
std::cout << "Prime numbers from 1'000'000 to 1'001'000: " << '\n';
for (int i: std::views::iota(1'000'000, 1'001'000) | std::views::filter(odd)
| std::views::filter(isPrime)) {
std::cout << i << " ";
}
std::cout << "\n\n";
std::cout << "20 prime numbers starting with 1'000'000: " << '\n';
for (int i: std::views::iota(1'000'000) | std::views::filter(odd)
| std::views::filter(isPrime)
| std::views::take(20)) {
std::cout << i << " ";
}
std::cout << '\n';
}

This is my iterative strategy:

  • line 14: Of course, I don’t know when I have 2020 primes greater than 10000001000000. To be on the safe side, I create 10001000 numbers. For obvious reasons, I displayed only each 100100th.
  • line 24: I’m only interested in the odd numbers; therefore, I remove the even numbers.
  • line 31: Now, it’s time to apply the next filter. The predicate isPrime (line 4) returns if a number is prime. As you can see from the output of the program, I was too eager. I got 7575 primes.
  • line 39: Laziness is a virtue. I use std::iota as an infinite number factory, starting with 10000001000000 and ask precisely for 2020 primes.