Boost Depth First Visit with avoidance and early exit

huangapple go评论66阅读模式
英文:

Boost Depth First Visit with avoidance and early exit

问题

Consider the following (directed) tree

0
|
1
/|
/ |
2' 3' 4'
| |
5' 6'

where all edges are directed downward.

The goal is to traverse this tree starting at 0 in a depth-first manner and return the first vertex with a pebble (indicated by an apostrophe ') and terminate the traversal. Furthermore, I would also like to avoid a given vertex.

For example, I want to find a pebble by avoiding the vertex 2. In that case the order of visiting is: 0 to 1, 1 to 2 (avoid, return to 1); 1 to 3 (pebble found, end). There should be no attempt to discover 5, 6, and 4.

I have implemented this with boost BGL via depth_first_visit. I can get the correct output to display on the console, for example:

Discover vertex 0
Discover vertex 1

Discover vertex 2
Avoiding vertex 2
Finished vertex 2

Discover vertex 3
Found a pebble on vertex 3 and stopping dfs.

However, the issue is that the depth_first_visit doesn't really stop at vertex 3. While I can make it not examine the edges 25 and 36 (and hence not discover 5 and 6) via a terminator function, the edge 14 will be examined (because examine_edge was invoked an all outgoing edges of 1 as soon as 1 was discovered) and therefore 4 will be discovered.

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>

struct pebbles{
    int num_pebbles = 1;
};

using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS,pebbles>;

struct Visitor : boost::default_dfs_visitor
{       
    using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
    using Edge = boost::graph_traits<Graph>::edge_descriptor;

    boost::optional<Vertex> avoid;
        
    void discover_vertex(Vertex s, Graph const &g){
        if (stop_dfs) return;
        std::cerr << "Discover vertex:   " << s << std::endl;
        
        if (s==avoid) {std::cerr << "Avoiding vertex:   " << s << std::endl; return; }
        
        if (g
展开收缩
.num_pebbles != 0 ){
stop_dfs = true; std::cerr << "Found a pebble on vertex: " << s << " and stopping dfs." << std::endl; return; } } //void examine_edge(Edge e, Graph const& ) const { std::cerr << "Examining edge: " << e << std::endl; } void finish_vertex(Vertex s, Graph const&) const { if (stop_dfs) return; std::cerr << "Finished vertex: " << s << std::endl; } //terminator function bool operator()(Vertex s, Graph const& g) const { return ((s == avoid) || (g
展开收缩
.num_pebbles != 0));
}; private: bool stop_dfs = false; }; int main(){ Graph g; //test graph g = {{0,1,2,3,4,5,6},{01,12,13,14,25,36}} boost::add_edge(0,1,g); boost::add_edge(1,2,g); boost::add_edge(1,3,g); boost::add_edge(1,4,g); boost::add_edge(2,5,g); boost::add_edge(3,6,g); g[0].num_pebbles = 0; g[1].num_pebbles = 0; Visitor peb_vis_termfunc; std::vector<boost::default_color_type> colormap(num_vertices(g)); peb_vis_termfunc.avoid = 2; //avoid vertex 2 boost::depth_first_visit(g,0,peb_vis_termfunc,colormap.data(),peb_vis_termfunc); return 0; }

It's easy to verify that the edge (1,4) is indeed examined by uncommenting the examine_edge function. I would like to not have to examine (1,4) at all and completely exit depth_first_visitor once a pebble is found.

I have read in the documentation that the intended way to force an early exit is to throw an exception, however I don't understand how to do that in this case (I'm a relative novice with C++ and STL/Boost).

I would also greatly appreciate any comments on style, good practice or improvements of the code above.

英文:

Consider the following (directed) tree

    0
|
1
/|\
/ | \
2&#39; 3&#39; 4&#39;
|  |
5&#39; 6&#39;

where all edges are directed downward.

The goal is to traverse this tree starting at 0 in a depth-first manner and return the first vertex with a pebble (indicated by an apostrophe ') and terminate the traversal. Furthermore, I would also like to avoid a given vertex.

For example, I want to find a pebble by avoiding the vertex 2. In that case the order of visiting is: 0 to 1, 1 to 2 (avoid, return to 1); 1 to 3 (pebble found, end). There should be no attempt to discover 5, 6, and 4.

I have implemented this with boost BGL via depth_first_visit. I can get the correct output to display on the console, for example:

Discover vertex  0
Discover vertex  1
Discover vertex  2
Avoiding vertex  2
Finished vertex  2
Discover vertex  3
Found a pebble on vertex 3 and stopping dfs.

However, the issue is that the depth_first_visit doesn't really stop at vertex 3. While I can make it not examine the edges 25 and 36 (and hence not discover 5 and 6) via a terminator function, the edge 14 will be examined (because examine_edge was invoked an all outgoing edges of 1 as soon as 1 was discovered) and therefore 4 will be discovered.

#include &lt;boost/graph/adjacency_list.hpp&gt;
#include &lt;boost/graph/graph_utility.hpp&gt;
#include &lt;iostream&gt;
struct pebbles{
int num_pebbles = 1;
};
using Graph = boost::adjacency_list&lt;boost::listS, boost::vecS, boost::directedS,pebbles&gt;;
struct Visitor : boost::default_dfs_visitor
{		
using Vertex = boost::graph_traits&lt;Graph&gt;::vertex_descriptor;
using Edge = boost::graph_traits&lt;Graph&gt;::edge_descriptor;
boost::optional&lt;Vertex&gt; avoid;
void discover_vertex(Vertex s, Graph const &amp;g){
if (stop_dfs) return;
std::cerr &lt;&lt; &quot;Discover vertex:   &quot; &lt;&lt; s &lt;&lt; std::endl;
if (s==avoid) {std::cerr &lt;&lt; &quot;Avoiding vertex:   &quot; &lt;&lt; s &lt;&lt; std::endl; return; }
if (g
展开收缩
.num_pebbles != 0 ){ stop_dfs = true; std::cerr &lt;&lt; &quot;Found a pebble on vertex: &quot; &lt;&lt; s &lt;&lt; &quot; and stopping dfs.&quot; &lt;&lt; std::endl; return; } } //void examine_edge(Edge e, Graph const&amp; ) const { std::cerr &lt;&lt; &quot;Examining edge: &quot; &lt;&lt; e &lt;&lt; std::endl; } void finish_vertex(Vertex s, Graph const&amp;) const { if (stop_dfs) return; std::cerr &lt;&lt; &quot;Finished vertex: &quot; &lt;&lt; s &lt;&lt; std::endl; } //terminator function bool operator()(Vertex s, Graph const&amp; g) const { return ((s == avoid) || (g
展开收缩
.num_pebbles != 0)); }; private: bool stop_dfs = false; }; int main(){ Graph g; //test graph g = {{0,1,2,3,4,5,6},{01,12,13,14,25,36}} boost::add_edge(0,1,g); boost::add_edge(1,2,g); boost::add_edge(1,3,g); boost::add_edge(1,4,g); boost::add_edge(2,5,g); boost::add_edge(3,6,g); g[0].num_pebbles = 0; g[1].num_pebbles = 0; Visitor peb_vis_termfunc; std::vector&lt;boost::default_color_type&gt; colormap(num_vertices(g)); peb_vis_termfunc.avoid = 2; //avoid vertex 2 boost::depth_first_visit(g,0,peb_vis_termfunc,colormap.data(),peb_vis_termfunc); return 0; }

It's easy to verify that the edge (1,4) is indeed examined by uncommenting the examine_edge function. I would like to not have to examine (1,4) at all and completely exit depth_first_visitor once a pebble is found.

I have read in the documentation that the intended way to force an early exit is to throw an exception, however I don't understand how to do that in this case (I'm a relative novice with C++ and STL/Boost).

I would also greatly appreciate any comments on style, good practice or improvements of the code above.

答案1

得分: 1

我相当肯定你可以像这样抛出一个异常:

if (g
展开收缩
.num_pebbles != 0 ) {
std::cerr << "在顶点找到了一个鹅卵石:" << s << ",停止dfs。" << std::endl; throw std::exception(); }

然后:

try {
    boost::depth_first_visit(g,0,peb_vis_termfunc,colormap.data(),peb_vis_termfunc);
}
catch (std::exception) {
    std::cout <<  "搜索结束。" << std::endl;
}

我对代码风格没有任何建议,代码看起来合理。不过,我注意到对于这样一个小例子来说,编译时间相当长(通常是因为使用了Boost库)。除非你计划在相同的上下文中添加更多功能,否则我唯一的建议就是自己编写,因为这样会更加精简。

英文:

I'm pretty sure you can just throw an exception like this:

if (g
展开收缩
.num_pebbles != 0 ) { std::cerr &lt;&lt; &quot;Found a pebble on vertex: &quot; &lt;&lt; s &lt;&lt; &quot; and stopping dfs.&quot; &lt;&lt; std::endl; throw std::exception(); }

and then:

try {
boost::depth_first_visit(g,0,peb_vis_termfunc,colormap.data(),peb_vis_termfunc);
}
catch (std::exception) {
std::cout &lt;&lt;  &quot;finished searching.&quot; &lt;&lt; std::endl;
}

And I don't have any suggestions about style, code looks reasonable, however, I did notice that the compile time was large for such a small example (as is usually the case with Boost library), and unless you are planning to add a lot more functionality within the same context, my only suggestion would be to write it yourself since it will be much leaner.

答案2

得分: 1

关于风格问题,我喜欢你的代码。它有很多微妙的选择,导致了良好可读的代码。巧妙地结合访问者和终止函数,由于及时的注释,效果很好。

最值得注意的反馈是,你为 depth_first_visit 包含了错误的标头 Boost Depth First Visit with avoidance and early exit

就我个人而言,我更喜欢简洁的代码。所以,我会更多地利用ADL,除非你需要支持用户定义/适应的图形,否则避免遍历图形特性。请注意,对于大多数图形类型,都有一个空顶点。以下是一个简化的版本,还限制了访问者的范围。

在Coliru上查看

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <iostream>

struct Node {
    int num_pebbles = 1;
};
using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, Node>;

int main() {
    Graph g;

    add_edge(0, 1, g);
    add_edge(1, 2, g);
    add_edge(1, 3, g);
    add_edge(1, 4, g);
    add_edge(2, 5, g);
    add_edge(3, 6, g);

    g[0].num_pebbles = 0;
    g[1].num_pebbles = 0;

    struct Visitor : boost::default_dfs_visitor {
        using Vertex = Graph::vertex_descriptor;
        struct Hit { Vertex v; };

        Vertex avoid = Graph::null_vertex();

        void discover_vertex(Vertex v, Graph const& g) const {
            std::cerr << "Visit vertex:   " << v << std::endl;

            if (v == avoid)
                std::cerr << "Avoided vertex: " << v << std::endl;
            else if (g[v].num_pebbles)
                throw Hit{v};
        }
    };

    try {
        std::vector<boost::default_color_type> colormap(num_vertices(g));

        depth_first_visit(g, 0, Visitor{.avoid = 2}, colormap.data());
    } catch (Visitor::Hit hit) {
        std::cerr << "Hit a pebble on vertex: " << hit.v << std::endl;
    }
}

打印

Visit vertex:   0
Visit vertex:   1
Visit vertex:   2
Avoided vertex: 2
Visit vertex:   5
Hit a pebble on vertex: 5

如果需要进一步的翻译,请告诉我。

英文:

On the topic of style, I love your code. It has many subtle choices that lead to good, readable code. Cleverly combining the visitor and termination function works well due the well-timed comment.

Most notable feedback I would have is that you include the wrong header for depth_first_visit Boost Depth First Visit with avoidance and early exit

Personally, I favour brief code. So, I'd leverage ADL a bit more and avoid going through the graph traits unless you need to support user-defined/adapted graphs. Note that there's a null vertex for most graph types. Here's a simplified version that also limits the scope of the visitor.

Live On Coliru

#include &lt;boost/graph/adjacency_list.hpp&gt;
#include &lt;boost/graph/depth_first_search.hpp&gt;
#include &lt;iostream&gt;
struct Node {
int num_pebbles = 1;
};
using Graph = boost::adjacency_list&lt;boost::listS, boost::vecS, boost::directedS, Node&gt;;
int main() {
Graph g;
add_edge(0, 1, g);
add_edge(1, 2, g);
add_edge(1, 3, g);
add_edge(1, 4, g);
add_edge(2, 5, g);
add_edge(3, 6, g);
g[0].num_pebbles = 0;
g[1].num_pebbles = 0;
struct Visitor : boost::default_dfs_visitor {
using Vertex = Graph::vertex_descriptor;
struct Hit { Vertex v; };
Vertex avoid = Graph::null_vertex();
void discover_vertex(Vertex v, Graph const&amp; g) const {
std::cerr &lt;&lt; &quot;Visit vertex:   &quot; &lt;&lt; v &lt;&lt; std::endl;
if (v == avoid)
std::cerr &lt;&lt; &quot;Avoided vertex: &quot; &lt;&lt; v &lt;&lt; std::endl;
else if (g[v].num_pebbles)
throw Hit{v};
}
};
try {
std::vector&lt;boost::default_color_type&gt; colormap(num_vertices(g));
depth_first_visit(g, 0, Visitor{.avoid = 2}, colormap.data());
} catch (Visitor::Hit hit) {
std::cerr &lt;&lt; &quot;Hit a pebble on vertex: &quot; &lt;&lt; hit.v &lt;&lt; std::endl;
}
}

Printing

Visit vertex:   0
Visit vertex:   1
Visit vertex:   2
Avoided vertex: 2
Visit vertex:   5
Hit a pebble on vertex: 5

huangapple
  • 本文由 发表于 2023年6月15日 15:15:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76479993.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定