我需要捕捉一个等式(例如2x ^ 2-1x)我有这段代码来捕获它
string eq;
cin>>eq;
为了解决这个问题,我需要将前一个字符串的每个字符拆分成一个数组,然后用循环来解决它。我想我知道如何做循环部分,但我怎么能把它分成一个数组呢?或者有更简单的方法来做到这一点吗?
答案 0 :(得分:4)
您实际上并没有尝试将字符串拆分为数组。一个阵列什么都不买你。你想要一个表达式树。或者至少是一个动态评估的递归下降解析。这会更容易但效率更低。
StackOverflow上的递归下降表达式解析器必须有很多问题/答案。使用搜索框获取想法。
为了完全矫枉过正,这里有一个公式评估函数的样本,包含动态(单字母)变量和一些测试用例。
它使用C ++ 14和Boost Spirit X3。这里的“递归下降”解析器是从PEG rules生成的,而不是手写的。
<强> Live On Coliru 强>
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <map>
using VarMap = std::map<char, double>;
namespace formula {
namespace detail {
using namespace boost::spirit::x3;
#define BIN(f) ([](auto &ctx) { _val(ctx) = f(_val(ctx), _attr(ctx)); })
#define BINOP(op) BIN(([](auto a, auto b) { return a op b; }))
#define IDENT ([](auto &ctx) { _val(ctx) = _attr(ctx); })
static VarMap var_map;
auto lookup = [](auto& ctx) { _val(ctx) = var_map.at(_attr(ctx)); };
rule<struct f_, double> factor {"factor"};
rule<struct t_, double> term {"term"};
rule<struct x_, double> expo {"expo"};
auto var = rule<struct _v, double> {"var"}
= alpha [lookup];
auto literal = !lit('-') >> double_;
auto simple = rule<struct _l, double> {"simple"}
= ('(' >> term >> ')') | var | literal;
auto expo_def
= simple [IDENT] >> *('^' >> expo)[BIN(pow)];
auto factor_def = expo [IDENT] >> *(
'*' >> factor [BINOP(*)]
| '/' >> factor [BINOP(/)]
| factor [BINOP(*)]
);
auto term_def = factor [IDENT] >> *(
'+' >> term [BINOP(+)]
| '-' >> term [BINOP(-)]
);
BOOST_SPIRIT_DEFINE(expo, factor, term)
auto expr = skip(space) [eps > term > eoi];
}
struct evaluation_error : std::runtime_error {
evaluation_error(std::string const& msg) : std::runtime_error(msg) {}
};
double eval(std::string const& formula, VarMap vars) {
using namespace std::string_literals;
detail::var_map = vars;
double value;
try {
bool ok = parse(begin(formula), end(formula), detail::expr, value);
assert(ok);
return value;
} catch(boost::spirit::x3::expectation_failure<std::string::const_iterator> const& e) {
throw evaluation_error("syntax: expect " + e.which() + " at '" + std::string(e.where(), formula.end()) + "'");
} catch(std::out_of_range const& e) {
throw evaluation_error("variable undefined");
} catch(std::exception const& e) {
throw evaluation_error("eval: "s + e.what());
}
}
}
int main() {
for (auto formula : { "", "0", "2", "x", "2x",
"x^2",
"2x^2",
"2x^2-1x",
"2x^2-sin x",
"x^(1/2)",
"(x^(1/2))^2",
})
try {
std::cout << "Function f(x) -> " << formula << "\n";
for (double x = 0; x < 10; x += 1)
std::cout << " - f(" << x << ") -> " << formula::eval(formula, {{'x', x}}) << "\n";
} catch(formula::evaluation_error const& e) {
std::cout << "Oops: " << e.what() << "\n";
}
}
打印
Function f(x) ->
- f(0) -> Oops: syntax: expect term at ''
Function f(x) -> 0
- f(0) -> 0
- f(1) -> 0
- f(2) -> 0
- f(3) -> 0
- f(4) -> 0
- f(5) -> 0
- f(6) -> 0
- f(7) -> 0
- f(8) -> 0
- f(9) -> 0
Function f(x) -> 2
- f(0) -> 2
- f(1) -> 2
- f(2) -> 2
- f(3) -> 2
- f(4) -> 2
- f(5) -> 2
- f(6) -> 2
- f(7) -> 2
- f(8) -> 2
- f(9) -> 2
Function f(x) -> x
- f(0) -> 0
- f(1) -> 1
- f(2) -> 2
- f(3) -> 3
- f(4) -> 4
- f(5) -> 5
- f(6) -> 6
- f(7) -> 7
- f(8) -> 8
- f(9) -> 9
Function f(x) -> 2x
- f(0) -> 0
- f(1) -> 2
- f(2) -> 4
- f(3) -> 6
- f(4) -> 8
- f(5) -> 10
- f(6) -> 12
- f(7) -> 14
- f(8) -> 16
- f(9) -> 18
Function f(x) -> x^2
- f(0) -> 0
- f(1) -> 1
- f(2) -> 4
- f(3) -> 9
- f(4) -> 16
- f(5) -> 25
- f(6) -> 36
- f(7) -> 49
- f(8) -> 64
- f(9) -> 81
Function f(x) -> 2x^2
- f(0) -> 0
- f(1) -> 2
- f(2) -> 8
- f(3) -> 18
- f(4) -> 32
- f(5) -> 50
- f(6) -> 72
- f(7) -> 98
- f(8) -> 128
- f(9) -> 162
Function f(x) -> 2x^2-1x
- f(0) -> 0
- f(1) -> 1
- f(2) -> 6
- f(3) -> 15
- f(4) -> 28
- f(5) -> 45
- f(6) -> 66
- f(7) -> 91
- f(8) -> 120
- f(9) -> 153
Function f(x) -> 2x^2-sin x
- f(0) -> Oops: variable undefined
Function f(x) -> x^(1/2)
- f(0) -> 0
- f(1) -> 1
- f(2) -> 1.41421
- f(3) -> 1.73205
- f(4) -> 2
- f(5) -> 2.23607
- f(6) -> 2.44949
- f(7) -> 2.64575
- f(8) -> 2.82843
- f(9) -> 3
Function f(x) -> (x^(1/2))^2
- f(0) -> 0
- f(1) -> 1
- f(2) -> 2
- f(3) -> 3
- f(4) -> 4
- f(5) -> 5
- f(6) -> 6
- f(7) -> 7
- f(8) -> 8
- f(9) -> 9
答案 1 :(得分:3)
std::string
已经是一个容器,你可以循环它:
std::string eq = "2x^2-1x";
for (char c : eq)
{
// use c
}
答案 2 :(得分:2)
你所说的是一个词法分析者。 Linux中有一个(微软现在也使用它)称为lex
或flex
(它实际上来自GNU)。它有一个C ++扩展。这将解析解析。
从我在你的例子中看到的内容:
[0-9]+ number
[a-z] letter
. operator
一旦你有了词法分析器,你需要一个编译器。这就是yacc
所在的位置。再次,有一个带有C ++扩展名,它被称为Bison(来自GNU)。
yacc
允许您编写几乎无论多么复杂的解析。 LALR解析器有限制 - 向前看从左到右最右边(派生),虽然Bison也为你提供GLR支持 - 广义从左到右最右边(派生)
为什么yacc
比编写自己的C / C ++代码更容易使用?因为它不需要你做任何工作就会优先考虑。当您撰写a + b * c
时,您知道首先需要计算b * c
,然后向该产品添加a
。如果你编写自己的代码就不那么容易了(如果你知道怎么做也不是那么难。)
yacc
的规则看起来有点像这样(没有必要的代码):
start: expr
expr: expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| expr '^' expr
| '+' expr
| '-' expr
| '(' expr ')'
在某个地方,您必须定义每个运营商的优先级。 '+'
和'-
&#39;这里最低,然后是'*'
和'/'
,然后是'^'
。 (此外'^'
还有另一个问题,它是&#34;先计算右侧&#34;,因此3^2^4
相当于3^(2^4)
但是我不知道这些细节。)
一些额外的信息以及整个此类项目的实际示例:
答案 3 :(得分:1)
std::string
以数组的形式提供对其内容的访问:
const string::size_type n=eq.length();
for(string::size_type i=0; i<n; ++i)
do_something(eq[i]);
// or see erenon's range-for example
然而,请注意parsing(分析字符串以理解方程式)会非常迅速地变得非常复杂;数组访问是最简单的部分。