how to parse and verify an ordered list of integers using qi

穿精又带淫゛_ 提交于 2019-12-05 08:53:33

This is at least a lot shorter already:

  • down to 28 LOC
  • no more locals
  • no more fusion vector at<> wizardry
  • no more inherited attributes
  • no more grammar class
  • no more manual iteration
  • using expectation points (see other) to enhance parse error reporting
  • this parser expressions synthesizes neatly into a vector<int> if you choose to assign it with %= (but it will cost performance, besides potentially allocating a largish array)

.

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

namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;

typedef std::string::iterator It;

int main(int argc, char** argv)
{
    std::string input("11 0.1\n"
            "14 0.78\n"
            "532 -3.6\n");

    int min=-1, max=0;
    {
        using namespace qi;
        using px::val;
        using px::ref;

        It begin(input.begin()), end(input.end());
        rule<It> index = int_ 
            [
                if_(ref(max) < _1)  [ ref(max) = _1 ] .else_ [ std::cout << _1 << val(" out of order\n") ],
                if_(ref(min) <  0)  [ ref(min) = _1 ]
            ] ;

        rule<It> other = char_(" \t") > float_ > eol;

        std::cout << "parse result: " << std::boolalpha 
                  << qi::parse(begin, end, index % other) << std::endl; 
    }
    std::cout << "min: " << min << "\nmax: " << max << std::endl;
    return 0;
}

Bonus

I might suggest taking the validation out of the expression and make it a free-standing function; of course, this makes things more verbose (and... legible) and my braindead sample uses global variables... -- but I trust you know how to use boost::bind or px::bind to make it more real-life

In addition to the above

  • down to 27 LOC even with the free function
  • no more phoenix, no more phoenix includes (yay compile times)
  • no more phoenix expression types in debug builds ballooning the binary and slowing it down
  • no more var, ref, if_, .else_ and the wretched operator, (which had major bug risk (at some time) due to the overload not being included with phoenix.hpp)
  • (easily ported to c++0x lambda's - immediately removing the need for global variables)

.

#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
namespace px = boost::phoenix;
namespace qi = boost::spirit::qi;
typedef std::string::iterator It;

int min=-1, max=0, linenumber=0;
void validate_index(int index)
{
    linenumber++;
    if (min < 0)     min = index;
    if (max < index) max = index;
    else             std::cout << index << " out of order at line " << linenumber << std::endl;
}

int main(int argc, char** argv)
{
    std::string input("11 0.1\n"
            "14 0.78\n"
            "532 -3.6\n");
    It begin(input.begin()), end(input.end());

    {
        using namespace qi;

        rule<It> index = int_ [ validate_index ] ;
        rule<It> other = char_(" \t") > float_ > eol;
        std::cout << "parse result: " << std::boolalpha 
                  << qi::parse(begin, end, index % other) << std::endl; 
    }
    std::cout << "min: " << min << "\nmax: " << max << std::endl;
    return 0;
}

I guess a much simpler way would be to parse the file using standard stream operations and then check the ordering in a loop. First, the input:

typedef std::pair<int, float> value_pair;

bool greater(const value_pair & left, const value_pair & right) {
    return left.first > right.first;
}

std::istream & operator>>(std::istream & stream, value_pair & value) {
    stream >> value.first >> value.second;
    return stream;
}

The use it like this:

std::ifstream file("your_file.txt");
std::istream_iterator<value_pair> it(file);
std::istream_iterator<value_pair> eof;

if(std::adjacent_find(it, eof, greater) != eof) {
    std::cout << "The values are not ordered" << std::endl;
}

I find this a lot simpler.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!