commit 82217b11787fd57a47e3bda301abc0f60c66255d
parent f339c85eb98d0673c7f75bb983b92c32b48ab9a4
Author: Shimmy Xu <shimmy.xu@shimmy1996.com>
Date: Sat, 27 Apr 2019 01:51:25 -0400
Add new post "enumerate() with C++"
Diffstat:
4 files changed, 161 insertions(+), 0 deletions(-)
diff --git a/content/posts/2019-04-27-enumerate-with-c-plus-plus.en.md b/content/posts/2019-04-27-enumerate-with-c-plus-plus.en.md
@@ -0,0 +1,79 @@
++++
+title = "enumerate() with C++"
+tags = ["c-plus-plus"]
+categories = ["programming"]
+draft = false
+date = 2019-04-27
+slug = "enumerate-with-c-plus-plus"
++++
+
+Quite a few programming languages provide ways to iterating through a container while keeping count of the number of steps taken, such as `enumerate` in Python:
+
+```python
+for i, elem in enumerate(v):
+ print(i, elem)
+```
+
+and `enumerate` under `std::iter::Iterator` trait in Rust:
+
+```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:
+
+```c++
+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:
+
+```c++
+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:
+
+```c++
+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
+
+```c++
+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:
+
+```c++
+for (auto [i, elem] : v | std::view::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::view::enumerate` to solve this problem once and for all.
diff --git a/i18n/en.toml b/i18n/en.toml
@@ -17,6 +17,9 @@ other = "Geekery"
[my-life]
other = "My Life"
+[programming]
+other = "Programming"
+
[site-related]
other = "Site Related"
@@ -24,6 +27,9 @@ other = "Site Related"
[arch-linux]
other = "Arch Linux"
+[c-plus-plus]
+other = "C++"
+
[design]
other = "Design"
diff --git a/i18n/zh.toml b/i18n/zh.toml
@@ -17,6 +17,9 @@ other = "折腾"
[my-life]
other = "日常"
+[programming]
+other = "编程"
+
[site-related]
other = "站点相关"
@@ -24,6 +27,9 @@ other = "站点相关"
[arch-linux]
other = "Arch Linux"
+[c-plus-plus]
+other = "C++"
+
[design]
other = "设计"
diff --git a/org/2019.org b/org/2019.org
@@ -112,6 +112,76 @@ Here's to another spectacular 2.9e+17 radiation periods of Caesium-133!
:EXPORT_FILE_NAME: 2019-01-03-2018-in-review.zh.md
:END:
+* Programming :@programming:
+** TODO enumerate() with C++ :c_plus_plus:
+:PROPERTIES:
+:EXPORT_HUGO_CUSTOM_FRONT_MATTER: :date 2019-04-27 :slug enumerate-with-c-plus-plus
+:END:
+
+*** DONE en
+:PROPERTIES:
+:EXPORT_TITLE: enumerate() with C++
+:EXPORT_FILE_NAME: 2019-04-27-enumerate-with-c-plus-plus.en.md
+:END:
+
+Quite a few programming languages provide ways to iterating through a container while keeping count of the number of steps taken, such as =enumerate= in Python:
+#+BEGIN_SRC python
+ for i, elem in enumerate(v):
+ print(i, elem)
+#+END_SRC
+and =enumerate= under =std::iter::Iterator= trait in Rust:
+#+BEGIN_SRC rust
+ for (i, elem) in v.iter().enumerate() {
+ println!("{}, {}", i, elem);
+ }
+#+END_SRC
+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:
+#+BEGIN_SRC c++
+ std::for_each(v.begin(), v.end(),
+ [i = 0](auto elem) mutable {
+ std::cout << i << ", " << elem << std::endl;
+ ++i;
+ });
+#+END_SRC
+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:
+#+BEGIN_SRC c++
+ for (auto [i, elem_it] = std::tuple{0, v.begin()}; elem_it != v.end();
+ ++i, ++elem_it) {
+ std::cout << i << ", " << *elem_it << std::endl;
+ }
+#+END_SRC
+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:
+#+BEGIN_SRC c++
+ 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;
+ }
+#+END_SRC
+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
+#+BEGIN_SRC c++
+ for (auto i = 0; auto elem : v) {
+ std::cout << i << ", " << elem << std::endl;
+ i++;
+ }
+#+END_SRC
+Meh, not that impressive. The new =<ranges>= library provides a more appealing way to achieve this:
+#+BEGIN_SRC c++
+ for (auto [i, elem] : v | std::view::transform(
+ [i = 0](auto elem) mutable { return std::tuple{i++, elem}; })) {
+ std::cout << i << ", " << elem << std::endl;
+ }
+#+END_SRC
+
+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::view::enumerate= to solve this problem once and for all.
+
* Geekery :@geekery:
** TODO Installing Gentoo :linux:
:PROPERTIES: