Aaaaaand Inbox is gone now. :(

enumerate() with C++

Quite a few programming languages provide ways to iterate through a container while keeping count of the number of steps taken, such as enumerate() in Python:

for i, elem in enumerate(v):
    print(i, elem)

and enumerate() under std::iter::Iterator trait in Rust:

for (i, elem) in v.iter().enumerate() {
    println!("{}, {}", i, elem);
}

This is just a quick note about how to do similar things in C++17 and later without declaring extra variables out of the for loop's scope.

The first way is to use a mutable lambda:

std::for_each(v.begin(), v.end(),
              [i = 0](auto elem) mutable {
                  std::cout << i << ", " << elem << std::endl;
                  ++i;
              });

This could be used with all the algorithms that guarantees in-order application of the lambda, but I don't like the dangling ++i that could get mixed up with other logic.

The second way utilizes structured binding in for loops:

for (auto [i, elem_it] = std::tuple{0, v.begin()}; elem_it != v.end();
     ++i, ++elem_it) {
    std::cout << i << ", " << *elem_it << std::endl;
}

We have to throw in std::tuple as otherwise compiler would try to create a std::initializer_list, which does not allow heterogeneous contents.

The third least fancy method is to just calculate the distance every time:

for (auto elem_it = v.begin(); elem_it != v.end(); ++elem_it) {
    auto i = std::distance(v.begin(), elem_it);
    std::cout << i << ", " << *elem_it << std::endl;
}

Since we have to copy paste the starting point twice, I like other counter based approaches better.

In C++20, we have the ability to add an init-statement in ranged-based for loops, so we can write something like

for (auto i = 0; auto elem : v) {
    std::cout << i << ", " << elem << std::endl;
    i++;
}

Meh, not that impressive. The new <ranges> library provides a more appealing way to achieve this:

for (auto [i, elem] : v | std::views::transform(
         [i = 0](auto elem) mutable { return std::tuple{i++, elem}; })) {
    std::cout << i << ", " << elem << std::endl;
}

I like the structured binding method and the <ranges> based method the most. It would be even better though if we can get a std::views::enumerate to solve this problem once and for all.

While listening to the pillows' "Come On, Ghost", it struck me that if I were to keep two cats and name them Beast and Ghost respectively, I'll have legitimate reason to call out "Come on, Ghost; Come on, Beast" randomly.

Test!

Unicode test: 🤣

I'm liking this a lot more than Mastodon

Checking time format

Hello Darkness, My Old Friend

With system wide dark modes becoming commonplace, I took the effort to tweak the color scheme of my blog and added a dark mode specific one using prefers-color-scheme in CSS. I also toyed around the idea of adding a user toggle using JavaScript per instructions here, but ultimately decided against it because of my (totally unjustified and groundless) distaste towards the language.

Color Usage Light Theme Dark Theme
Accent #700000 #8fffff
Background #f7f3e3 #080c1c
Text #2e2d2b #d1d2d4
Code Background #e3dacb #1c2534
Border 1 #e7e3d3 #181c2c
Border 2 #d7d3c3 #282c3c

Writing CSS is a such tiring endeavor, but on the bright side, picking colors is a surprisingly relaxing activity. The light mode color scheme now has reduced contrast, and I updated the isso style sheets with matching colors. Yes, I only inverted the colors in dark mode and did not reduce the font weights because of the peculiar way in which human vision work. Part of me already screams heresy when I look at the color codes formed by three numbers that seem to have no connection whatsoever—they are like dissonant chords that cause itches in brain—so I need them to at least sum up to a nice number.

Wissen ist Nacht!