blog

My blog at www.shimmy1996.com

git clone git://git.shimmy1996.com/blog.git
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:
Acontent/posts/2019-04-27-enumerate-with-c-plus-plus.en.md | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mi18n/en.toml | 6++++++
Mi18n/zh.toml | 6++++++
Morg/2019.org | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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: