是否有范围视图可以迭代两个映射的交集并使用公共键和两个值执行函数

问题描述 投票:0回答:1

假设我们有两个映射,它们具有一些公共键但值不同,我想迭代两个映射的“交集”,其中键相同。然后我想执行一个变换函数

f(key, value_in_map1, value_in_map2)

我想使用

range views
来完成它,这样我就可以进一步进行管道化。我在 range-v3 中尝试了
ranges::views::set_intersection
,但它没有按预期工作:

#include <map>
#include <iostream>
#include <range/v3/all.hpp>

int main () {
    std::map<int, int> m1 {{0, 0}, {1, 1}, {2, 2}};
    std::map<int, int> m2 {{0, 0}, {2, 1}, {3, 2}};

    auto res = ranges::views::set_intersection(m1, m2);
    for(auto&& p : res) {
        std::cout << p.first << ' ' << p.second << '\n';
        // output: 0 0
    }

    /* Here is what I expect for the desired view
    auto res = map_intersection(m1, m2);
    for(auto&& [key, value_in_m1, value_in_m2] : res) {
        std::cout << p.first << ' ' << p.second << '\n';
        // expected output:
        // 0 0 0
        // 2 2 1
    }
    // my desired syntax
    map_intersection(m1, m2) 
      | ranges::views::transform([](int key, int value_in_m1, int value_in_m2) {
                                      // use the values here
        })
      | more_views
    */

    return 0;
}

https://godbolt.org/z/3cbMY9n7b显示了上面的代码。

如果range-v3中不存在这样的视图(上面注释代码中的

map_intersection
),如何实现?

c++ algorithm c++17 range range-v3
1个回答
0
投票

v3 或 STL 中没有可用的视图或算法。主要问题是所有

set_*
函数仅返回第一个范围的值。在您的示例中,您正在比较这对地图,因此您只能得到
0 0
结果。您可以通过使用自定义投影仪或自定义比较器来解决此问题,但是您会得到
0 0
2 2
(来自第一个范围),而
2 1
(来自第二个范围)缺失。

您需要的是类似

set_intersection_transform
的东西,它在返回结果之前转换两个迭代器。使用 v3 编写这样的视图很困难,您可以查看现有的 sourcemanual 关于如何编写自定义视图。实现这样的视图可能超出了 SO 的范围。

请注意,

set_intersection
在 C++ 20 中不是作为视图实现的,而是作为算法实现的。您可以轻松地根据您的需要调整 cppreference 中的示例。这是一个复制粘贴,删除了所有 C++ 20 内容(包括返回类型),并添加了我添加的转换功能。尽管它缺乏所有 v3 约束并且不是视图,但它可能是您工作的起点。

#include <range/v3/all.hpp>

struct set_intersection_transform_fn {
  template <typename I1, typename S1, typename I2, typename S2, typename O,
            typename Comp, typename Proj1, typename Proj2, typename Func>
  constexpr void operator()(I1 first1, S1 last1, I2 first2, S2 last2, O result,
                            Comp comp, Proj1 proj1, Proj2 proj2,
                            Func func) const {
    while (!(first1 == last1 or first2 == last2)) {
      if (std::invoke(comp, std::invoke(proj1, *first1),
                      std::invoke(proj2, *first2))) {
        ++first1;
      } else if (std::invoke(comp, std::invoke(proj2, *first2),
                             std::invoke(proj1, *first1))) {
        ++first2;
      } else {
        *result = std::invoke(func, *first1, *first2), ++first1, ++first2,
        ++result;
      }
    }
  }

  template <typename R1, typename R2, typename O, typename Comp, typename Proj1,
            typename Proj2, typename Func>
  void operator()(R1&& r1, R2&& r2, O result, Comp comp, Proj1 proj1,
                  Proj2 proj2, Func func) const {
    (*this)(ranges::begin(r1), ranges::end(r1), ranges::begin(r2),
            ranges::end(r2), std::move(result), std::move(comp),
            std::move(proj1), std::move(proj2), std::move(func));
  }
};

inline constexpr set_intersection_transform_fn set_intersection_transform{};

输出为

0 0 0
2 2 1
:

std::map<int, int> m1{{0, 0}, {1, 1}, {2, 2}};
std::map<int, int> m2{{0, 0}, {2, 1}, {3, 2}};
std::vector<std::tuple<int, int, int>> result;

const auto proj = &std::map<int, int>::value_type::first;
const auto transform = [](const auto& a, const auto& b) {
  return std::tuple{a.first, a.second, b.second};
};

set_intersection_transform(m1, m2, std::back_inserter(result), std::less<>{},
                            proj, proj, transform);

for (const auto& [key, value_in_m1, value_in_m2] : result) {
  std::cout << key << " " << value_in_m1 << " " << value_in_m2 << std::endl;
}
© www.soinside.com 2019 - 2024. All rights reserved.