簡體   English   中英

通過使用boost spirit qi解析器迭代填充BGL圖

[英]Iteratively populate a BGL graph by use of a boost spirit qi parser

這個問題是“具有提升精神的抽象語法樹的迭代更新”的后續。

已知:

  • 解析器語法允許遞歸

要求是:

  • 解析器的AST必須是BGL圖。
  • 輸入可以是每個解析器步驟的一對多符號

思路:

  • 關於精神解析為BGL圖的一些基本思想如下所示:使用boost圖庫:如何創建圖...但不能完全滿足要求,因為我希望能夠迭代地解析一對多符號。
  • 猜猜BGL圖和精神解析器必須彼此了解一些東西才能在正確的位置填充數據。 首先想到的是,解析器必須能夠處理圖頂點。
  • 諸如使用語義動作/ qi :: locals之類的解決方案可能適用,但我不確定這是否足以允許解析器迭代地處理圖形。

有沒有人有一些想法如何解決這個問題,還是指向某個方向?

謝謝

簡而言之:這很難實現。

如果想要將一些數據解析為BGL圖,最簡單的方法可能是使用BGL圖形適配器vector_as_graph。 給定此適配器並且鑒於boost精神解析器可以解析為向量,那么它是可能的。

enum { r, s, t, u, v, w, x, y, N };
char name[] = "rstuvwxy";
typedef std::vector < std::list < int > > Graph;
Graph g(N);
g[r].push_back(v);
g[s].push_back(r);
g[s].push_back(r);
g[s].push_back(w);
g[t].push_back(x);
g[u].push_back(t);
g[w].push_back(t);
g[w].push_back(x);
g[x].push_back(y);
g[y].push_back(u);
boost::print_graph(g, name);

我使用的解決方案是在boost精神域中解析給定的語法,然后執行到AST的轉換,以便表示為BGL圖。

最后的想法:應該注意的是,提升精神和BGL領域的學習曲線相當陡峭。 基於此,如果分開,可讀性和實現更容易。 不是說它無法解決。

回應你的

最后的想法:應該注意的是,提升精神和BGL領域的學習曲線相當陡峭。 基於此,如果分開,可讀性和實現更容易。 不是說它無法解決。

在你的回答中,我同意。 保持簡單的關鍵是分開關注點。 僅僅因為鳳凰城讓你幾乎可以將任何東西綁在你的解析器中,並不意味着它是個好主意[1]

也許這個示例應用程序可以作為靈感?

住在Coliru

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

using namespace boost;
typedef adjacency_list<setS, vecS, directedS> Graph;

std::vector<std::string> generate_file_data();
void merge_into(std::istream&& is, Graph& into);

int main() {
    Graph merged;

    for(auto& input: generate_file_data())         
        merge_into(std::istringstream(input), merged);

    print_graph(merged);
}

如您所見,它會讀取許多(不同的)文件並將邊mergedmerged圖中。

當然,在在線演示中我們沒有輸入文件,因此我在20個頂點( generate_file_data() )中生成5個隨機邊緣的5個隨機圖。

有趣的是在merge_into

#include <boost/spirit/include/qi.hpp>

void merge_into(std::istream&& is, Graph& into) {
    namespace qi  = boost::spirit::qi;
    namespace phx = boost::phoenix;

    phx::function<edge_merge_f> merge_edges;
    boost::spirit::istream_iterator f(is >> std::noskipws), l;

    bool ok = qi::phrase_parse(f, l, 
            (qi::int_ >> *qi::int_) [ merge_edges(qi::_1, qi::_2, phx::ref(into)) ] % qi::eol,
            qi::blank);

    assert(ok);
}

這是處理精神的唯一代碼。 正如您所看到的,它根本不了解BGL。 這個邏輯留給了一個“回調”函數對象edge_merge_f ,這也很簡單:

struct edge_merge_f {
    template <typename...> struct result { typedef void type; }; // for BOOST_RESULT_OF_USE_TR1:

    template<typename V, typename Edges, typename G>
        void operator()(V const& v, Edges const& edges, G& into) const {
            for (auto e : edges)
                if (!add_edge(v, e, into).second)
                    std::cout << "Duplicate edge: (" << v << "," << e << ")\n";
        }
};

更新作為獎勵,通過將EdgeCallback作為boost::functionEdgeCallback 版本傳遞給它來完全將merge_adjacencies與Graph類型解耦 當然,效率會降低,但它通過保持分離來表明我的意思。

所以你有它。 這是我機器上的輸出(我沒有播種隨機引擎所以它是可重復的): Live On Coliru

Duplicate edge: (9,1)
Duplicate edge: (0,4)
0 --> 1 2 4 9 
1 --> 
2 --> 
3 --> 1 7 
4 --> 1 
5 --> 1 6 
6 --> 1 3 5 
7 --> 5 6 8 
8 --> 3 
9 --> 1 2 

如果更改Graph typedef以使用vecS作為邊緣容器,則不必更改任何其他內容,結果是: Live On Coliru

0 --> 4 9 1 4 2 
1 --> 
2 --> 
3 --> 7 1 
4 --> 1 
5 --> 1 6 
6 --> 1 3 5 
7 --> 5 6 8 
8 --> 3 
9 --> 1 2 1 

[1]我想我把你與提升精神聯系在一起:“語義行為是邪惡的”? 之前:)

完整清單

對於后代(還包括generate_file_data() ):

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

using namespace boost;
typedef adjacency_list<setS/*vecS*/, vecS, directedS> Graph;

std::vector<std::string> generate_file_data();
void merge_into(std::istream&& is, Graph& into);

int main() {
    Graph merged;

    for(auto& input: generate_file_data())         
        merge_into(std::istringstream(input), merged);

    print_graph(merged);
}

////////////////////////////////
// Generating random test data
#include <boost/graph/random.hpp>
#include <boost/random.hpp>

std::vector<std::string> generate_file_data() {
    std::vector<std::string> data;

    mt19937 prng(42);
    for (int i=0; i<5; ++i) {
        std::ostringstream oss;

        Graph g;
        generate_random_graph(g, 10, 4, prng);

        for (auto v : make_iterator_range(vertices(g))) {
            oss << v << " ";
            for (auto const& e : make_iterator_range(out_edges(v, g))) oss << target(e, g) << " ";
            oss << "\n";
        }

        data.push_back(oss.str());
    }

    return data;
}

////////////////////////////////
// Merging edges
namespace {
    struct edge_merge_f {
        template <typename...> struct result { typedef void type; }; // for BOOST_RESULT_OF_USE_TR1:

        template<typename V, typename Edges, typename G>
            void operator()(V const& v, Edges const& edges, G& into) const {
                for (auto e : edges)
                    if (!add_edge(v, e, into).second)
                        std::cout << "Duplicate edge: (" << v << "," << e << ")\n";
            }
    };
}

#include <boost/spirit/include/qi.hpp>

void merge_into(std::istream&& is, Graph& into) {
    namespace qi  = boost::spirit::qi;
    namespace phx = boost::phoenix;

    phx::function<edge_merge_f> merge_edges;
    boost::spirit::istream_iterator f(is >> std::noskipws), l;

    bool ok = qi::phrase_parse(f, l, 
            (qi::int_ >> *qi::int_) [ merge_edges(qi::_1, qi::_2, phx::ref(into)) ] % qi::eol,
            qi::blank);

    assert(ok);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM