jjzjj

c++ - 如何概括具有变体/访问者的树结构

coder 2024-02-19 原文

这是我问题的第 2 部分,最初发布于 here .感谢@sehe 的澄清和帮助。我最终得到了下面的代码,但我不知道如何将它简化为具有变体和访问者的通用解决方案。非常感谢帮助/建议。谢谢。

#include "stdafx.h"
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <boost/format.hpp>
#include <boost/variant.hpp>


template <typename T> class A 
{
public:
    typename T L;
    typename std::shared_ptr<T> Lptr;
    using tlist = std::vector<std::shared_ptr<T>>;
    A(std::string n = "") : _n(n){}
    A(const A& another) : _n(another._n){};
    A(A&& a) : _n(a._n){ _lst = std::move(another._lst); }
    tlist &lst() { return _lst; }
    void emplace_back(std::shared_ptr<T> wp) {
        _lst.emplace_back(wp);
    }
    std::string n() const { return _n; }
private:
    tlist _lst;
    std::string _n;
};

/*
suppose I have following tree structure
    Store
        Shelves
            Products on the shelve
*/
using lA = A<boost::blank>; // product
using lB = A<lA>;           // shelf
using lC = A<lB>;           // store
using lAp = std::shared_ptr<lA>;
using lBp = std::shared_ptr<lB>;
using lCp = std::shared_ptr<lC>;


void printIt(lAp p, int indent){
    for (int i = 0; i < indent; ++i)
        std::cout << '\t';
    std::cout << p->n() << std::endl;
}


void printIt(lBp p, int indent){
    for (int i = 0; i < indent; ++i)
        std::cout << '\t';
    std::cout << p->n() << std::endl;;
    std::for_each(begin(p->lst()), end(p->lst()), [&](lAp i){
        printIt(i, indent + 1); }
    );
}


void printIt(lCp p, int indent){
    for (int i = 0; i < indent; ++i)
        std::cout << '\t';
    std::cout << p->n() << std::endl;
    std::for_each ( begin(p->lst()), end(p->lst()), [&](lBp i)
    {
        printIt(i, indent + 1);
    });
}


int main() {
    using storage = boost::variant<lAp, lBp, lCp>;
    std::vector<lCp> stores;
    for (int s = 0; s < 5; ++s) {
        lCp store(new lC((boost::format("store %1%") % s).str()));
        stores.emplace_back(store);
        for (int i = 0; i < 3; ++i) {// ten shelves in the store
            lBp shelf(new lB((boost::format("shelf %1%") % i).str()));
            store->emplace_back(shelf);
            for (int j = 0; j < 2; ++j) // twenty producs per shelf
                shelf->emplace_back(std::shared_ptr<lA>(new lA((boost::format("product %1%") % j).str())));
        }
    }
    std::for_each(begin(stores), end(stores), [](lCp p){printIt(p,0); });
    int i;
    std::cin >> i;
}

最佳答案

  1. KISS第一

    我不确定所有多态性的目标是什么,包括静态的和动态的。我会说如果你的类型结构是这样固定的,只需使用:

    Live On Coliru

    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    
    namespace SimpleDomain {
    
        struct Product {
            std::string name;
        };
    
        struct Shelf {
            std::string name;
            std::vector<Product> _products;
        };
    
        struct Store {
            std::string name;
            std::vector<Shelf> _shelves;
        };
    
        std::ostream& operator<<(std::ostream& os, Product const& p) {
            return os << "\t\t" << p.name << "\n";
        }
        std::ostream& operator<<(std::ostream& os, Shelf const& s) {
            os << "\t" << s.name << "\n";
            for (auto& p : s._products) os << p;
            return os;
        }
        std::ostream& operator<<(std::ostream& os, Store const& s) {
            os << s.name << "\n";
            for (auto& sh : s._shelves) os << sh;
            return os;
        }
    }
    
    int main() {
        std::vector<SimpleDomain::Store> stores = {
            { "store 1", {
                    { "shelf 1", { { "product 1" }, { "product 2" }, { "product 3" }, } },
                    { "shelf 2", { { "product 4" }, { "product 5" }, { "product 6" }, } },
                 },
            },
            { "store 2", {
                    { "shelf 1", { { "product 7" }, { "product 8" }, { "product 9" }, } },
                    { "shelf 2", { { "product 10" }, { "product 11" }, { "product 12" }, } },
                 },
            }
        };
    
        std::for_each(begin(stores), end(stores), 
                [](SimpleDomain::Store const& p){std::cout << p;});
    }
    

    打印

    store 1
        shelf 1
            product 1
            product 2
            product 3
        shelf 2
            product 4
            product 5
            product 6
    store 2
        shelf 1
            product 7
            product 8
            product 9
        shelf 2
            product 10
            product 11
            product 12
    
  2. 完全通用,无动态多态性:

    在这里你可以使用递归变体来更通用:

    Live On Coliru

    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <boost/variant.hpp>
    
    namespace GenericDomain {
    
        namespace Tag {
            struct Store{};
            struct Shelf{};
            struct Product{};
        }
    
        template <typename Kind> struct Node;
    
        using Store   = Node<Tag::Store>;
        using Shelf   = Node<Tag::Shelf>;
        using Product = Node<Tag::Product>;
    
        using Tree = boost::variant<
            boost::recursive_wrapper<Product>,
            boost::recursive_wrapper<Store>,
            boost::recursive_wrapper<Shelf>
        >;
    
        template <typename Kind> struct Node {
            std::string name;
            std::vector<Tree> children;
        };
    
        template <> struct Node<Tag::Product> {
            std::string name;
        };
    
        std::ostream& operator<<(std::ostream& os, Tag::Store)   { return os << "Store";   }
        std::ostream& operator<<(std::ostream& os, Tag::Shelf)   { return os << "\tShelf";   }
        std::ostream& operator<<(std::ostream& os, Tag::Product) { return os << "\t\tProduct"; }
    
        template <typename Kind> std::ostream& operator<<(std::ostream& os, Node<Kind> const& n) {
            os << Kind{} << ": " << n.name << "\n";
            for (auto& child : n.children) os << child;
            return os;
        }
        std::ostream& operator<<(std::ostream& os, Product const& p) {
            return os << Tag::Product{} << ": " << p.name << "\n";
        }
    }
    
    int main() {
        using namespace GenericDomain;
        std::vector<Store> stores = {
            Store { "store 1", {
                    Shelf { "shelf 1", { Product { "product 1" },  Product { "product 2" },  Product { "product 3" }, } },
                    Shelf { "shelf 2", { Product { "product 4" },  Product { "product 5" },  Product { "product 6" }, } },
                 },
            },
            Store { "store 2", {
                    Shelf { "shelf 1", { Product { "product 7" },  Product { "product 8" },  Product { "product 9" }, } },
                    Shelf { "shelf 2", { Product { "product 10" }, Product { "product 11" }, Product { "product 12" }, } },
                 },
            }
        };
    
        std::for_each(begin(stores), end(stores), 
                [](GenericDomain::Store const& p){std::cout << p;});
    }
    

    打印

    Store: store 1
        Shelf: shelf 1
            Product: product 1
            Product: product 2
            Product: product 3
        Shelf: shelf 2
            Product: product 4
            Product: product 5
            Product: product 6
    Store: store 2
        Shelf: shelf 1
            Product: product 7
            Product: product 8
            Product: product 9
        Shelf: shelf 2
            Product: product 10
            Product: product 11
            Product: product 12
    

    可以看到我们可以检测到节点的类型。当然,没有什么能阻止我们制作奇怪的层次结构:

    std::vector<Store> stores = {
        Store { "store 1", {
            Shelf { "shelf 1", { 
                Product { "product 1" },
                Store { "store 2", {
                    Shelf { "shelf 1", { Product { "product 7" },  Product { "product 8" },  Product { "product 9" }, } },
                    Shelf { "shelf 2", { Product { "product 10" }, Product { "product 11" }, Product { "product 12" }, } },
                }, },
                Product { "product 3" },
            } },
            Shelf { "shelf 2", { Product { "product 4" },  Product { "product 5" },  Product { "product 6" }, } },
        }, },
    };
    

    为了一般地处理缩进,创建一个有状态的访问者:

    std::ostream& operator<<(std::ostream& os, Tag::Store)   { return os << "Store";   }
    std::ostream& operator<<(std::ostream& os, Tag::Shelf)   { return os << "Shelf";   }
    std::ostream& operator<<(std::ostream& os, Tag::Product) { return os << "Product"; }
    
    struct print_vis {
        size_t indent = 0;
        std::ostream& _os;
    
        using result_type = void;
    
        template <typename Kind> void operator()(Node<Kind> const& n) const {
            _os << std::string(indent, ' ') << Kind{} << ": " << n.name << "\n";
            print_vis sub { indent+4, _os };
            for (auto& child : n.children) sub(child);
        }
    
        void operator()(Product const& p) const {
            _os << std::string(indent, ' ') << Tag::Product{} << ": " << p.name << "\n";
        }
    
        void operator()(Tree const& tree) const {
            boost::apply_visitor(*this, tree);
        }
    

    打印: Live On Coliru

    Store: store 1
        Shelf: shelf 1
            Product: product 1
            Store: store 2
                Shelf: shelf 1
                    Product: product 7
                    Product: product 8
                    Product: product 9
                Shelf: shelf 2
                    Product: product 10
                    Product: product 11
                    Product: product 12
            Product: product 3
        Shelf: shelf 2
            Product: product 4
            Product: product 5
            Product: product 6
    
  3. 没有变体,只有动态多态性

    使用与上面的 GenericDomain 树相同的“怪异”树:

    Live On Coliru

    #include <iostream>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <memory>
    
    namespace DynamicDomain {
    
        struct Node;
        using Tree = std::shared_ptr<Node>;
    
        struct Node {
            virtual std::string type() const = 0;
            std::string name;
            std::vector<Tree> children;
    
            template <typename... Child>
            Node(std::string name, Child&&... children) : 
                name(std::move(name)), children { std::forward<Child>(children)... }
            { }
        };
    
        struct Product : Node { using Node::Node; virtual std::string type() const { return "Product"; } };
        struct Shelf   : Node { using Node::Node; virtual std::string type() const { return "Shelf"; } };
        struct Store   : Node { using Node::Node; virtual std::string type() const { return "Store"; } };
    
        struct print_vis {
            size_t indent;
            std::ostream* _os;
    
            using result_type = void;
    
            void operator()(Tree const& tree) const {
                if (tree) (*this) (*tree); else *_os << "[null]";
            }
            void operator()(Node const& node) const {
                *_os << std::string(indent, ' ') << node.type() << ": " << node.name << "\n";
                print_vis sub { indent+4, _os };
                for (auto const& child : node.children) sub(child);
            }
        };
    
        std::ostream& operator<<(std::ostream& os, Tree const& n) {
            print_vis{0, &os} (n);
            return os;
        }
    }
    
    int main() {
        using namespace DynamicDomain;
        std::vector<Tree> stores = {
            std::make_shared<Store> ("store 1", 
                    std::make_shared<Shelf> ("shelf 1",
                            std::make_shared<Product> ("product 1"),
                            std::make_shared<Store> ("store 2",
                                    std::make_shared<Shelf> ("shelf 1",  std::make_shared<Product> ("product 7"),  std::make_shared<Product> ("product 8"),  std::make_shared<Product> ("product 9") ),
                                    std::make_shared<Shelf> ("shelf 2",  std::make_shared<Product> ("product 10"), std::make_shared<Product> ("product 11"), std::make_shared<Product> ("product 12") )
                                    ),
                            std::make_shared<Product> ("product 3")
                            ),
                    std::make_shared<Shelf> ("shelf 2",  std::make_shared<Product> ("product 4"),  std::make_shared<Product> ("product 5"),  std::make_shared<Product> ("product 6") )
                    ),
        };
    
        std::for_each(begin(stores), end(stores), 
                [](DynamicDomain::Tree const& p){ std::cout << p; });
    }
    

    这不是我的“整洁”想法,而且效率可能低得多 - 尽管它确实允许可空节点和子树共享。

关于c++ - 如何概括具有变体/访问者的树结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37197445/

有关c++ - 如何概括具有变体/访问者的树结构的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  8. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  9. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  10. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

随机推荐