【问题标题】:Using ranges-v3 to implement DFS使用ranges-v3实现DFS
【发布时间】:2019-08-24 08:21:29
【问题描述】:

我对使用 range-v3 构建和查询线性四叉树数据结构很感兴趣。我已经能够使用库中的现有视图成功地使用 range-v3 构建线性四叉树数据结构。我很高兴能够将查询逻辑表达为视图适配器,因为您可以通过推进派生范围的 RandomAccessIterator 来迭代四叉树中的节点,这有助于将查询行为与四叉树的结构分开。

我的视图适配器有一个参数:一个用户定义的 lambda 谓词函数,用于评估节点并确定是步入还是退出。步进导致评估子节点,而步进导致访问下一个兄弟节点(或可能是节点的父节点的下一个兄弟节点),直到成功评估叶节点或我们通过根节点“退出”。 (您可以将其视为 DFS 模式。)

因此,我们可以根据 RandomAccessIterator(来自派生范围)和 Sentinel(与另一个 Iterator 相对)来定义此范围。

这里有一些显示整体结构的精简代码。 (如果缺少成员数据/结构,我深表歉意):

template<typename Rng, typename Fun>
class quadtree_query_view
  : public ranges::view_adaptor<quadtree_query_view<Rng, Fun>, Rng>
{
    friend ranges::range_access;

    using base_iterator_t = ranges::iterator_t<Rng>;

    ranges::semiregular_t<Fun> fun;
    uint tree_depth;

    struct query_termination_adaptor : public ranges::adaptor_base
    {
        query_termination_adaptor() = default;
        query_termination_adaptor(uint tree_depth) : tree_depth(tree_depth) {};

        uint tree_depth;

        uint end(quadtree_query_view const&) {
            return tree_depth;
        }
    };

    struct query_adaptor : public ranges::adaptor_base
    {
        query_adaptor() = default;
        query_adaptor(ranges::semiregular_t<Fun> const& fun) : fun(fun) {};

        ranges::semiregular_t<Fun> fun;

        bool exited = false;
        uint current_node_depth = 0;

        base_iterator_t begin(quadtree_query_view const& rng) {
            return ranges::begin(rng.base());
        }

        // TODO: implement equal?
        // TODO: implement empty?

        auto read(base_iterator_t const& it) const 
        {
            return *it; // I'm not concerned about the value returned by this range yet.
        }

        CONCEPT_REQUIRES(ranges::RandomAccessIterator<base_iterator_t>())
        void next(base_iterator_t& it ){
            if (fun(*it)) { // Step in
                // Advance base iterator (step in)
                // Increment current_node_depth
            } else {  // Step out
                // Advance base iterator (step out)
                // Set "exited = true" if stepping out past root node.
                // Decrement current_node_depth
            }
        }
    };

public:
    quadtree_query_view() = default;

    quadtree_query_view(Rng&& rng, uint tree_depth, Fun fun)
      : quadtree_query_view::view_adaptor{std::forward<Rng>(rng)}
      , tree_depth(tree_depth)
      , fun(std::move(fun))
    {}

    query_adaptor begin_adaptor() const {
        return {std::move(fun)};
    }

    query_termination_adaptor end_adaptor() const {
        return {tree_depth};
    }
};

我正在尝试找出完成此实施的最后几个步骤:

  • 我的范围不满足 Range 概念,因为我的迭代器/哨兵对没有实现 WeaklyEqualityComparable 要求。这样做的最佳方法是什么?

  • 是否需要为query_adaptor 实现equal 成员方法?两个迭代器参数对应什么?

  • 我假设我需要为query_adaptor 实现empty 成员方法。这是查询退出标准逻辑的去向吗?根据文档,段参数需要是与哨兵关联的类型。这是否与query_termination_adaptor::end() 返回的类型相同,例如uint?还是需要另一种类型?

感谢您分享的任何见解。我真的很高兴看到范围被合并到 C++20 中!

【问题讨论】:

    标签: c++ range-v3


    【解决方案1】:

    啊。

    我可以使用default_sentinel 解决我的问题。由于query_adaptor 意味着从根节点开始并沿单个方向迭代,因此我可以一起删除end_adaptorquery_termination_adaptor。我只需为适配器实现bool equal(default_sentinel) const { ... } 方法,我就可以确定是否满足查询退出条件。

    我仍然不确定为什么尝试实现自定义哨兵类型会给我带来问题。但是,除了拥有tree_depth 之外,它并没有提供比default_sentinel 更多的功能。

    【讨论】:

      猜你喜欢
      • 2022-07-06
      • 2021-08-21
      • 1970-01-01
      • 1970-01-01
      • 2016-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-09
      相关资源
      最近更新 更多