【问题标题】:Boost::Spirit::QI parser: index of parsed elementBoost::Spirit::QI 解析器:解析元素的索引
【发布时间】:2012-10-07 15:01:48
【问题描述】:

是否可以(使用 Boost::Spirit::QI)从逗号分隔的字符串中解析数字,以便获得每个解析数字的索引?

假设我有一个字符串"23,123,65,1",我想将这些数字中的每一个插入到给定位置(0、1、2、3)的矩阵中。一种方法是将数字解析为 std::vector,然后将它们复制到矩阵行,但这并不是特别快。

目前我正在使用矢量变体:

Matrix data(10, 4);
int row = 0;
int col = 0;
std::string str = "23,123,65,1";
std::vector<double> res;
if (qi::parse(str.begin(), str.end(), qi::double_ % ',', res))
{
  std::for_each(res.begin(), res.end(), [&col, &data, &row](double elem) {

      data(row, col) = elem;
      col++;
});
}

如果解析器有一个采用 lambda 函数或类似功能的成功回调,那就太棒了。

【问题讨论】:

    标签: c++ boost boost-spirit


    【解决方案1】:

    有很多方法。

    • 我通常推荐的是使用经过深思熟虑的 repeat(n) 表达式和直接暴露的容器属性(如 vector&lt;vector&lt;double&gt; &gt;)。

    • 似乎正在寻找的是带有状态的语义动作。 (这是来自 lex/yacc 的常见做法)。

    我在下面的三个完整演示中处理这些方法(1.、2. 和 3.)

    • 一种高级技术是使用自定义点,让 Spirit 直接将您的 Matrix 类型视为容器属性,并使用 spirit::traits 覆盖它的插入逻辑。对于这种方法,我参考了这个答案:pass attribute to child rule in boost spirit

    使用inherited attributes

    这是一个相对简单的方法:

    1. 直接解析成vector&lt;vector&lt;double&gt; &gt;(full code live online)

      qi::rule<It, Matrix::value_type(size_t cols), qi::blank_type> row;
      qi::rule<It, Matrix(size_t rows,size_t cols), qi::blank_type> matrix;
      
      row    %= skip(char_(" \t,")) [ repeat(_r1) [ double_ ] ];
      matrix %= eps // [ std::cout << phx::val("debug: ") << _r1 << ", " << _r2 << "\n" ]
             >> repeat(_r1) [ row(_r2) >> (eol|eoi) ];
      

      用法:

      if (qi::phrase_parse(f,l,parser(10, 4),qi::blank, m))
          std::cout << "Wokay\n";
      else
          std::cerr << "Uhoh\n";
      
    2. 类似,但采用 Matrix 结构 (full code live here)

      struct Matrix
      {
          Matrix(size_t rows, size_t cols) : _cells(), _rows(rows), _cols(cols) { }
      
          double       & data(size_t col, size_t row)       { return _cells.at(row).at(col); } 
          const double & data(size_t col, size_t row) const { return _cells.at(row).at(col); } 
      
          size_t columns() const { return _cols; }
          size_t rows()    const { return _rows; }
      
          std::vector<std::vector<double> > _cells;
          size_t _rows, _cols;
      };
      
      BOOST_FUSION_ADAPT_STRUCT(Matrix, (std::vector<std::vector<double> >,_cells))
      

      用法

      Matrix m(10, 4);
      
      if (qi::phrase_parse(f,l,parser(m.rows(),m.columns()),qi::blank, m))
          std::cout << "Wokay\n";
      else
          std::cerr << "Uhoh\n";
      

    使用语义动作/qi::locals

    3.这是更多的工作,但可能更灵活。您将定义一个多态可调用类型以在给定单元格中插入一个值:

    struct MatrixInsert
    {
        template <typename, typename, typename, typename> struct result { typedef bool type; };
        template <typename Matrix, typename Row, typename Col, typename Value>
            bool operator()(Matrix &m, Row& r, Col& c, Value v) const
            {
                if (r < m.rows() && c < m.columns())
                {
                    m.data(r, c++) = v;
                    return true; // parse continues
                }
                return false;    // fail the parse
            }
    };
    
    BOOST_PHOENIX_ADAPT_CALLABLE(matrix_insert, MatrixInsert, 4)
    

    最后一行使它成为 phoenix 惰性函数,因此您可以在语义操作中使用它而无需奇怪的绑定语法:

    qi::rule<It, Matrix(), qi::blank_type, qi::locals<size_t /*_a: row*/, size_t/*_b: col*/> > matrix;
    matrix = eps    [ _a = 0 /*current row*/ ]
         >> (
                eps     [ _b = 0 /*current col*/ ] 
             >> double_ [ _pass = matrix_insert(_val, _a, _b, _1) ] % ','
            ) % (eol    [ ++_a /*next row*/])
         ;
    

    完整的代码是 live on liveworkspace.org

    【讨论】:

    • 你是巫师吗?第三个选项正是我所需要的,我不知道你可以做这样的事情(它与 Eigen 矩阵一起工作得很好)。希望有一个选项可以多次为您投票。非常感谢。
    • 好的,非常感谢。我喜欢在 SO 上锻炼我的 C++ fu :)
    • 如果您有兴趣,您的版本将代码加速了 2.6 倍!
    • 哇。我只能想象那是因为以前你必须做很多复制:)
    • @gnzlbg 很伤心 :( 我非常有信心 sn-ps 捕获了基本位
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多