我需要开发一个解决线性方程的程序。这些节目 首先读取一个整数 n ,这是方程的数量。 然后程序读取包含方程的 n 行。 例如,程序的输入如下:
3
2x1+3x2+4x3=16
1x1+2x2+1x3=8
3x1+1x2+2x3=13
任何操作都应首先将每个等式转换为 正确的形式。等式本身应具有以下属性
变量按字母顺序从左到右排序:
3x2+2x1+4x3=16
应该是
2x1+3x2+4x3=16
任何变量只应出现一次:
4x1+3x2-2x1+4x3=16
应该是
2x1+3x2+4x3=16
等式中只应出现一个常数项,它应该是 在右边:
2x1+3x2+5+4x3-11=10
应该是
2x1+3x2+4x3=16
等于1时的系数或-1数字1是可选的:
1x1+3x2-1x3=10
可以输入
x1+3x2-x3=10
到目前为止,我所做的工作如下:
#include<iostream>
#include<string>
#include<sstream>
#include<cstdlib>
using namespace std;
int main() {
int n;
cin >> n;
string eqn[100];
//get eq from user
for (int i = 0; i < n; i++) {
cin >> eqn[i];
}
size_t s = 0;
size_t y = 0;
for (int i = 0; i < n; i++) {
for (int x = 1; x <= ((eqn[i].length() - ((eqn[i].length() - 3) / 4)) / 3); x++)
{
int counter = 0;
ostringstream ss;
ss << x;
string j = ss.str();
for (int t = 0; t < eqn[i].length(); t++) {
y = eqn[t].find("x" + j, y + 1);
if (y < eqn[i].length()) { counter++; }
}
for (int o = 1; o <= counter; o++) {
s = eqn[i].find("x" + j, s + 1);
string x1 = eqn[i].substr(s - 1, 3);
string x2 = x2 + x1;
cout << x1;
}
}
cout << endl;
}
int k; cin >> k;
return 0;
}
但事情变得过于复杂,我不确定这是否是正确的方法..
是否有更好的方法来操作find()
,substr()
以外的字符串等式?
我该如何处理这个问题?
答案 0 :(得分:3)
我开始使用Syntax Diagram定义(我不会称之为)语言:
然后我把它翻译成一个手写的解析器。
parse-equation.cc
:
#include <iostream>
#include <algorithm>
int parseDigit(const char *&la)
{
switch (*la) {
case '0': ++la; return 0;
case '1': ++la; return 1;
case '2': ++la; return 2;
case '3': ++la; return 3;
case '4': ++la; return 4;
case '5': ++la; return 5;
case '6': ++la; return 6;
case '7': ++la; return 7;
case '8': ++la; return 8;
case '9': ++la; return 9;
default: return -1; // ERROR!
}
}
int parseNumber(const char *&la)
{
int value = parseDigit(la);
if (value < 0) return -1; // ERROR!
for (;;) {
const int digit = parseDigit(la);
if (digit < 0) return value;
value *= 10; value += digit;
}
}
struct Term {
int coeff; // -1 ... missing
int expon; // -1 ... missing -> ERROR
Term(int coeff = -1, int expon = 0): coeff(coeff), expon(expon) { }
};
Term parseTerm(const char *&la)
{
Term term;
term.coeff = parseNumber(la);
if (*la == 'x') {
++la;
term.expon = parseDigit(la);
if (term.coeff < 0) term.coeff = 1; // tolerate missing coeff. for x
}
return term;
}
struct Expression {
bool error;
int coeffs[10];
Expression(bool error = false): error(error)
{
std::fill(std::begin(coeffs), std::end(coeffs), 0);
}
};
Expression parseExpression(const char *&la)
{
Expression expr;
int sign = +1;
do {
const Term term = parseTerm(la);
if (term.expon < 0) return Expression(true); // ERROR!
expr.coeffs[term.expon] += sign * term.coeff;
switch (*la) {
case '+': sign = +1; ++la; break;
case '-': sign = -1; ++la; break;
case '=': break;
default: return Expression(true); // ERROR!
}
} while (*la != '=');
++la;
// parse right hand side
const int result = parseNumber(la);
if (result < 0) return Expression(true); // ERROR!
expr.coeffs[0] -= result;
// check for extra chars
switch (*la) {
case '\n': ++la;
case '\0': break;
default: return Expression(true); // ERROR!
}
return expr;
}
std::ostream& operator<<(std::ostream &out, const Expression &expr)
{
if (expr.error) out << "ERROR!";
else {
bool empty = true;
for (size_t i = 9; i; --i) {
const int coeff = expr.coeffs[i];
if (coeff) out << coeff << 'x' << i << std::showpos, empty = false;
}
if (empty) out << 0;
out << std::noshowpos << '=' << -expr.coeffs[0];
}
return out;
}
int main()
{
const char *samples[] = {
"2x1+3x2+4x3=16",
"1x1+2x2+1x3=8",
"3x1+1x2+2x3=13",
"2x1+3x2+5+4x3-11=10",
"x1+3x2-x3=10"
};
enum { nSamples = sizeof samples / sizeof *samples };
for (size_t i = 0; i < nSamples; ++i) {
std::cout << "Parse '" << samples[i] << "'\n";
const char *la = samples[i];
std::cout << "Got " << parseExpression(la) << std::endl;
}
return 0;
}
使用g++
进行了编译,并在cygwin中进行了测试:
$ g++ -std=c++11 -o parse-equation parse-equation.cc
$ ./parse-equation
Parse '2x1+3x2+4x3=16'
Got 4x3+3x2+2x1=16
Parse '1x1+2x2+1x3=8'
Got 1x3+2x2+1x1=8
Parse '3x1+1x2+2x3=13'
Got 2x3+1x2+3x1=13
Parse '2x1+3x2+5+4x3-11=10'
Got 4x3+3x2+2x1=16
Parse 'x1+3x2-x3=10'
Got -1x3+3x2+1x1=10
$
注意:
可以使用std::strtol()
代替parseDigit()
和parseNumber()
。这会显着减少代码。
我使用const char*
作为“读头”la
(...缩写为“向前看”)。纯粹的C ++方式可能是std::stringstream
或std::string::iterator
,但可能是,我对这些新奇的东西还不够用。对我来说,const char*
是最直观的方式......
右侧的结果简单地从x 0 的系数中减去。因此,右侧是0,或者x 0 的负系数变为右侧。对于我的漂亮打印operator<<()
,我选择了后一种选择。
错误处理相当差,并且可以通过更详细的信息来增强解析失败的原因。我把它留下来不要再“吹”代码了。
可以轻松增强解析器以在任何适当的位置跳过空白区域。这样可以提高便利性。
在当前状态下,右侧的结果可能不是负数。我把这个扩展留作练习。