5

(Boost::Spirit::QI を使用して) コンマ区切りの文字列から数値を解析して、解析された各数値のインデックスを取得することは可能ですか?

文字列が"23,123,65,1"あり、これらの各数値を行列の特定の位置 (0、1、2、3) に挿入するとします。それを行う 1 つの方法は、数値を解析して 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++;
});
}

ラムダ関数または同様の機能を使用する成功のコールバックがパーサーにあれば、すばらしいことです。

4

1 に答える 1

12

いくつかのアプローチがあります。

  • 代わりに私が通常推奨するのはrepeat(n)、直接公開されたコンテナ属性( などvector<vector<double> >) を使用してよく考えられた式を使用することです。

  • あなたが探しているように見えるのは、状態を伴うセマンティックアクションです。(これは lex/yacc に由来する一般的な方法です)。

これらのアプローチを、以下の 3 つの完全なデモ(1.、2.、および 3.)で扱います。

  • 高度な手法は、カスタマイズ ポイントMatrixを使用して、Spirit が型をコンテナー属性として直接扱い、spirit::traits. このアプローチについては、この回答を参照します: pass attribute to child rule in boost spirit

継承された属性の使用

比較的簡単なアプローチを次に示します。

  1. に直接解析するvector<vector<double> >(完全なコードはオンラインで公開されています)

    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構造体を適応させます(完全なコードはこちらにあります

    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*/])
     ;

完全なコードは、再びliveworkspace.org で公開されています

于 2012-10-07T19:25:58.873 に答える