语义动作后丢弃解析结果
Discarding parsed result after semantic action
在 Boost.Spirit 中,只需执行以下操作即可从流读取到 std::vector
:
#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
int main(){
std::string const v_str = "AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
(*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
assert( v.size() == 3 and v[2] == "CC" );
}
不过碰巧因为输入格式的缘故,我提前知道了元素个数,应该可以预留vector中的space。
比如输入字符串是“3 AA BB CC”,可以预先分配三个元素。
问题是如何将这些额外信息传递给向量并优化后面的push_back
(例如避免重新分配)。
我尝试的是在开始时解析一个整数,然后在执行 reserve
的地方将语义操作关联到它。
std::string const v_str = "3 AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
sqi::int_[([&](int i){v.reserve(i);})] >>
(*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
问题是在语义操作之后整数没有被忽略,从我的测试中我可以看到它试图在保留之后将结果(示例中的 3
)推入向量中。
另一种解决方法是向 phrase_parse
函数添加另一个参数,但这似乎有点矫枉过正。
那么,我怎样才能解析 Boost.Spirit 中的某些内容并只执行语义操作而不将结果发送到接收器变量?
即使可以做到这一点,我也不确定这是否是正确的方法。
您可以创建假向量,它只会在插入和解析相同文本两次时计算在内:
#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
struct fake_vector
{
typedef std::string value_type;
fake_vector() : counter(0) {}
std::size_t end() const {return 0;};
void insert(std::size_t, std::string){ ++counter; }
std::size_t counter;
};
int main(){
std::string const v_str = "AA BB CC";
auto it = begin(v_str);
fake_vector fv;
bool r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, fv);
assert(fv.counter == 3);
std::vector<std::string> v;
v.reserve(fv.counter);
it = begin(v_str);
r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
assert( v.size() == 3 and v[2] == "CC" );
}
好吧,我似乎不得不解构 Spirit 的简单设施并将所有转换为语义动作,这在途中产生了其他问题(例如 lexeme[+char_]
映射到 std::vector<char>
预期 std::string
.
{
std::string const v_str = "AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
(*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
assert( v.size() == 3);
assert( v[2] == "CC" );
}
{
std::string const v_str = "3 AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
sqi::int_[([&](int i){v.reserve(i);})] >>
(*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
assert( v.size() == 3 );
assert( v[2] == "CC" );
}
因为这修改了 phrase_parse
的最后一个参数,我不妨放一个虚拟 int
.
感谢@sehe 和@drus 指向我的链接并找到了关于 qi::omit
的信息,我意识到我可以关联一个语义动作然后忽略结果。
我必须处理的格式是多余的(大小与元素的数量是多余的),所以我必须在语义上无论如何都要省略一些东西。
using namespace sqi;
std::string const v_str = "3 AA BB CC";
{
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_] >> *lexeme[+(char_-' ')],
space, v
);
assert( v.size() == 3 and v[2] == "CC" );
}
但这并不意味着我不能将省略(冗余)的部分用于优化目的或一致性检查。
{
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_[([&](int n){v.reserve(n);})]] >> *lexeme[+(char_-' ')],
space, v
);
assert( v.size() == 3 and v[2] == "CC" );
}
我同意语义动作是邪恶的,但在我看来只有当它们改变了接收器对象的状态时。
可以争辩说 reserve
不会改变向量的状态。
事实上,通过这种方式我可以通过 reserve
优化内存使用,也可以通过使用 repeat
而不是无限的 kleene*
来优化解析器的执行。 (Apparently repeat
can be more efficient).
{
std::vector<std::string> v;
auto it = begin(v_str);
int n;
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_[([&](int nn){v.reserve(n = nn);})]] >> repeat(phx::ref(n))[lexeme[+(char_-' ')]],
space, v
);
assert( n == v.size() and v.size() == 3 and v[2] == "CC" );
}
(unsing phx::ref
是基本的,因为 n
的评估必须延迟)
在 Boost.Spirit 中,只需执行以下操作即可从流读取到 std::vector
:
#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
int main(){
std::string const v_str = "AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
(*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
assert( v.size() == 3 and v[2] == "CC" );
}
不过碰巧因为输入格式的缘故,我提前知道了元素个数,应该可以预留vector中的space。 比如输入字符串是“3 AA BB CC”,可以预先分配三个元素。
问题是如何将这些额外信息传递给向量并优化后面的push_back
(例如避免重新分配)。
我尝试的是在开始时解析一个整数,然后在执行 reserve
的地方将语义操作关联到它。
std::string const v_str = "3 AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
sqi::int_[([&](int i){v.reserve(i);})] >>
(*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
问题是在语义操作之后整数没有被忽略,从我的测试中我可以看到它试图在保留之后将结果(示例中的 3
)推入向量中。
另一种解决方法是向 phrase_parse
函数添加另一个参数,但这似乎有点矫枉过正。
那么,我怎样才能解析 Boost.Spirit 中的某些内容并只执行语义操作而不将结果发送到接收器变量?
即使可以做到这一点,我也不确定这是否是正确的方法。
您可以创建假向量,它只会在插入和解析相同文本两次时计算在内:
#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
struct fake_vector
{
typedef std::string value_type;
fake_vector() : counter(0) {}
std::size_t end() const {return 0;};
void insert(std::size_t, std::string){ ++counter; }
std::size_t counter;
};
int main(){
std::string const v_str = "AA BB CC";
auto it = begin(v_str);
fake_vector fv;
bool r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, fv);
assert(fv.counter == 3);
std::vector<std::string> v;
v.reserve(fv.counter);
it = begin(v_str);
r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
assert( v.size() == 3 and v[2] == "CC" );
}
好吧,我似乎不得不解构 Spirit 的简单设施并将所有转换为语义动作,这在途中产生了其他问题(例如 lexeme[+char_]
映射到 std::vector<char>
预期 std::string
.
{
std::string const v_str = "AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
(*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
assert( v.size() == 3);
assert( v[2] == "CC" );
}
{
std::string const v_str = "3 AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
sqi::int_[([&](int i){v.reserve(i);})] >>
(*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
assert( v.size() == 3 );
assert( v[2] == "CC" );
}
因为这修改了 phrase_parse
的最后一个参数,我不妨放一个虚拟 int
.
感谢@sehe 和@drus 指向我的链接并找到了关于 qi::omit
的信息,我意识到我可以关联一个语义动作然后忽略结果。
我必须处理的格式是多余的(大小与元素的数量是多余的),所以我必须在语义上无论如何都要省略一些东西。
using namespace sqi;
std::string const v_str = "3 AA BB CC";
{
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_] >> *lexeme[+(char_-' ')],
space, v
);
assert( v.size() == 3 and v[2] == "CC" );
}
但这并不意味着我不能将省略(冗余)的部分用于优化目的或一致性检查。
{
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_[([&](int n){v.reserve(n);})]] >> *lexeme[+(char_-' ')],
space, v
);
assert( v.size() == 3 and v[2] == "CC" );
}
我同意语义动作是邪恶的,但在我看来只有当它们改变了接收器对象的状态时。
可以争辩说 reserve
不会改变向量的状态。
事实上,通过这种方式我可以通过 reserve
优化内存使用,也可以通过使用 repeat
而不是无限的 kleene*
来优化解析器的执行。 (Apparently repeat
can be more efficient).
{
std::vector<std::string> v;
auto it = begin(v_str);
int n;
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_[([&](int nn){v.reserve(n = nn);})]] >> repeat(phx::ref(n))[lexeme[+(char_-' ')]],
space, v
);
assert( n == v.size() and v.size() == 3 and v[2] == "CC" );
}
(unsing phx::ref
是基本的,因为 n
的评估必须延迟)