【问题标题】:Convert an mpl sequence of sequences into a trie将 mpl 序列序列转换为 trie
【发布时间】:2013-02-19 12:55:13
【问题描述】:

问题看起来很简单,基本上我有一个序列,比如:

typedef mpl::vector<
  mpl::vector<mpl::_1, mpl::_2>,
  mpl::vector<mpl::_1, mpl::_2, mpl::_3>,
  mpl::vector<mpl::_2, mpl::_1>,
  mpl::vector<mpl::_2, mpl::_2>,
  mpl::vector<mpl::_2, mpl::_2, mpl::_3>
> seq;

我想做的是将其转换为 trie,最终结果类似于:

mpl::map<
  mpl::pair<mpl::_1, 
    mpl::map<
      mpl::pair<mpl::_2,
        mpl::map<
          mpl::pair<TERMINAL, T>,
          mpl::pair<mpl::_3,
            mpl::map<
              mpl::pair<TERMINAL, T>
            >
          >
        >
      >
    >
  >
  mpl::pair<mpl::_2, 
    mpl::map<
      mpl::pair<mpl::_1,
        mpl::map<
          mpl::pair<TERMINAL, T>
        >
      >,
      mpl::pair<mpl::_2,
        mpl::map<
          mpl::pair<TERMINAL, T>,
          mpl::pair<mpl::_3,
            mpl::map<
              mpl::pair<TERMINAL, T>
            >
          >
        >
      >
    >
  >
>

所以,问题是,这可能吗(我认为不可能)?如果可能,我错过了哪些黑暗咒语?

编辑:如果上述从序列序列到 trie 的转换不清楚,让我看看我是否可以用简单的英语陈述它(通常更难。)基本上主序列中的每个序列都由一些类型组成(_1_2 等)转换后的版本是折叠常见前缀的特里树。可能是附图有帮助..

EDIT2:感谢@Yakk,希望现在问题更清楚了......

【问题讨论】:

  • 我看不出你想要的转换是什么。请提供实际的具体示例和伪代码。
  • @Yakk,已更新 - 这有帮助吗?基本上我正在尝试在图片中构建给定的树,以便我可以使用给定的序列导航(mpl::vector&lt;mpl::_1, mpl::_2&gt; 以获取 TERMINAL 类型的实例)
  • 如果缺少子序列 1 和 4,则该结构是否等同于生成的结构?如果我理解正确的话,主序列是路径序列,并且在不存在时生成节点(缺少路径mpl::vector&lt;mpl::_1&gt;mpl::vector&lt;mpl::_2&gt;
  • @AndyProwl,最终结果应该允许您查找任何给定的序列以获得 TERMNIAL 类型,如果缺少任何,我看不出这怎么可能?
  • @Nim:我想知道的是从主序列中删除第一个和第四个序列是否会产生相同的结果。在我看来,这些序列是多余的,不是吗?如果没有,为什么 mpl::vector&lt;mpl::_1&gt;mpl::vector&lt;mpl::_2&gt; 也没有出现?

标签: c++ templates boost template-meta-programming boost-mpl


【解决方案1】:

你去吧:

struct Terminal;

template < typename Trie, typename First, typename Last,
           typename Enable = void >
struct insertInTrie_impl
{
    typedef typename
        mpl::deref<First>::type key;

    typedef typename 
        mpl::at<
            Trie,
            key
        >::type subTrieOrVoid; // would be easier if "at" supported Default

    typedef typename
        mpl::if_<
            boost::is_same< subTrieOrVoid, mpl::void_ >,
            mpl::map<>,
            subTrieOrVoid
        >::type subTrie;

    typedef typename
        mpl::insert<
            Trie,
            mpl::pair<
                key, typename
                insertInTrie_impl<
                    subTrie, typename
                    mpl::next<First>::type,
                    Last
                >::type
            >
        >::type type;
};

template < typename Trie, typename First, typename Last >
struct insertInTrie_impl< Trie, First, Last, typename 
    boost::enable_if< boost::is_same<First, Last> >::type >
    : mpl::insert<
        Trie,
        mpl::pair< Terminal, Terminal >
        // I'm not sure what you want in your terminal node
    >
{};

template < typename Trie, typename Seq >
struct insertInTrie
    : insertInTrie_impl< 
        Trie, typename 
        mpl::begin<Seq>::type, typename 
        mpl::end<Seq>::type
    >
{};


template < typename SeqOfSeq >
struct constructTrie
    : mpl::fold< 
        SeqOfSeq,
        mpl::map<>,
        insertInTrie< mpl::_1, mpl::_2 >
    >
{};

insertInTrie_impl 是一个递归元函数,它使用迭代器将序列插入到现有的树中。 insertInTrie 接受一个调用insertInTrie_impl 的序列。 constructTrieinsertInTrie 应用于给定序列中的所有序列,从一个空的 trie 开始。

伪代码如下:

Trie insertInTrie_impl(trie, first, last)
{
    if (first == last)
    {
        trie.insert(Terminal, Terminal);
        return trie;
    }

    key = *first;

    subTrie = trie[key];
    if (subTrie = void) // key not found
    {
        subTrie = emptyTrie;
    }

    trie.insert(key, insertInTrie_impl(subTrie, ++first, last))

    return trie;
}

Trie insertInTrie(trie, seq)
{
    return insertInTrie_impl(trie, seq.begin(), seq.end();
}

Trie constructTrie(seqOfSeq)
{
    return fold(seqOfSeq, emptyTrie, insertInTrie);
}

最后,一个示例使用:

int main()
{
    typedef mpl::vector<
        mpl::vector<mpl::_1, mpl::_2>,
        mpl::vector<mpl::_1, mpl::_2, mpl::_3>,
        mpl::vector<mpl::_2, mpl::_1>,
        mpl::vector<mpl::_2, mpl::_2>,
        mpl::vector<mpl::_2, mpl::_2, mpl::_3>
    > seqOfSeq;

    typedef constructTrie< seqOfSeq >::type bigTrie;

    BOOST_MPL_ASSERT(( 
        mpl::has_key<
            mpl::at< 
                mpl::at< 
                    bigTrie, 
                    mpl::_1
                >::type, 
                mpl::_2
            >::type, 
            Terminal
        > ));
    BOOST_MPL_ASSERT(( 
        mpl::has_key<
            mpl::at< 
                mpl::at< 
                    mpl::at< 
                        bigTrie, 
                        mpl::_1
                    >::type,
                    mpl::_2
                >::type, 
                mpl::_3
            >::type, 
            Terminal
        > ));
    BOOST_MPL_ASSERT(( 
        mpl::has_key<
            mpl::at< 
                mpl::at< 
                    bigTrie, 
                    mpl::_2
                >::type,
                mpl::_2
            >::type, 
            Terminal
        > ));
}

【讨论】:

  • +1,我要花一整天的时间才能弄清楚这一点! ;)
  • @Nim:一旦你习惯了元函数的工作方式,这实际上并不难。我添加了一些伪代码以更简单的方式解释 mpl 代码。
  • 完美,我得到了这个工作,终端类型可以作为最后一个参数在嵌套序列中传递,我只需要添加另一个间接来从序列中剥离它并传播它。再次感谢!
【解决方案2】:

所以答案是“是的,这是可能的”。

写 add_to_trie。它接受一个可能为空的 trie 和一个元素(类型序列)并返回一个添加了该元素的 trie。

在一个空的 trie 和一些序列上测试 add_to_trie,并在一些其他手工制作的案例上进行测试。通用前缀:("A")("A","B"),无通用前缀:("A","A")("B","A"),较短的无通用前缀:("A" ,"B")("B"),同一事物的两个副本:("A")("A") 等

写累加。它需要一个值、一个二元仿函数和一个序列。如果对序列的每个元素 s 应用 value = functor(value, s),则返回 value。

通过将 1 到 5 相加并打印结果来累积测试。

将两者组合起来。

这可能会破坏您的模板递归堆栈,并且每个步骤都写得正确,但它会起作用。

首先编写上面对字符串进行操作可能会有所帮助。然后使功能起作用。然后转换为对类型进行操作。

我敢打赌boost 已经写了一个合适的accumulate

【讨论】:

  • boost 确实提供了accumulate,我想我现在的问题是如何以正确的顺序放置咒语以获得最终结果...
猜你喜欢
  • 1970-01-01
  • 2015-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多