【问题标题】:How to use a STL algorithm to replace the for loop?如何使用 STL 算法替换 for 循环?
【发布时间】:2020-08-25 09:04:41
【问题描述】:

如何用 STL 算法替换 for 循环?这里我想将向量valsvals[i]的值赋给nodeList中的nodeList[i]->val。如果我用vals[i] * 2来代替呢?

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

int main()
{
    TreeNode node1(1);
    TreeNode node2(2);
    TreeNode node3(3);
    node1.left = &node2;
    node1.right = &node3;

    vector<TreeNode*> nodeList = {&node1, &node2, &node3};
    vector<int> vals = {1, 3, 2};
    for (int i = 0; i < vals.size(); i++) {
        nodeList[i]->val = vals[i];
        // nodeList[i]->val = vals[i] * 2;
    }
}

【问题讨论】:

标签: c++ for-loop c++11 c++-standard-library


【解决方案1】:

你在这里做什么,你同时遍历两个容器。

虽然在现代 C++ 中可以对单个容器进行非常简洁的迭代,但我从内心深处不建议对这两个容器进行迭代 - 还没有准备好。 This question 列出了各种可能的解决方案,但我不太确定。

可以在手动维护第二个索引或迭代器的同时尝试范围循环:

  auto i = vals.begin();
  for (auto & node: nodeList) {
    node->val = *i++;
  }

这是更少的代码,并且在迭代诸如 std::deque 之类的东西时会更快,其中(与 std::vector 不同)直接索引很昂贵。但也可以让代码保持原样并等待下一个 C++ 标准。

【讨论】:

    【解决方案2】:

    for 循环的标准“算法”是std::for_each。它本质上是一个 for 循环的包装器:

    std::for_each(begin(nodeList), end(nodeList), [&vals, i = 0](auto& it) mutable {
        it->val = vals[i++];
    });
    

    由于您需要一次迭代两个集合,您仍然需要一个索引变量(本例中为i),或者在每次迭代中使用std::distance 来计算它。

    【讨论】:

      【解决方案3】:

      您的第一个案例是副本,第二个案例是转换。问题是你需要transform 输出迭代器。

      auto as_vals = std::views::transform(nodeList, std::mem_fn(&Node::val));
      std::copy(vals.begin(), vals.end(), as_vals.begin());
      std::transform(vals.begin(), vals.end(), as_vals.begin(), [](int i){ return i * 2; });
      

      boost

      auto as_val = boost::make_transform_iterator(nodeList.begin(), std::mem_fn(&Node::val));
      std::copy(vals.begin(), vals.end(), as_val);
      std::transform(vals.begin(), vals.end(), as_val, [](int i){ return i * 2; });
      

      如果你没有 C++20 或 boost 的访问权限,你可以写类似的东西

      template <typename Iterator, typename T>
      class member_iterator
      {
          Iterator wrapped;
          using base_t = typename std::iterator_traits<Iterator>::value_type;
          T (base_t::*mem);
      public
      
          using value_type = T;
          using pointer = T*;
          using reference = T&;
          using difference_type = typename std::iterator_traits<Iterator>::difference_type;
          using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
      
          member_iterator(Iterator wrapped, T (base_t::*mem)) : wrapped(wrapped), mem(mem) {} 
      
          T & operator*() const { return (*wrapped).*mem; }
          member_iterator& operator++() { ++wrapped; return *this; }
          // etc.
      };
      

      【讨论】:

      • 只能用标准库完成吗,拜托。因为提升并不总是可用。
      • 你可以自己写一个transform_iterator,或者更具体的通过成员间接实现
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-21
      • 1970-01-01
      • 2019-06-12
      • 1970-01-01
      • 2016-03-14
      • 2019-09-13
      • 1970-01-01
      相关资源
      最近更新 更多