4
\$\begingroup\$

I made a simple math expressions solver that using a "for" loop iterates over all the characters and stores the numbers and signs in std::vector. It give support for unlimited parentheses and does multiplications and division before other calculations. I really appreciate if you give any suggestions. Here's the code:

using std::cout;
using std::endl;
using std::cin;
using std::vector;
using std::string;

void input(vector<float> &, vector<char> &, vector<int> &, vector<int> &, string);

int numLenght(int index, string poly)
{
    int lenght = 0;
    for (int i = index; i < poly.length(); i++)
    {
        char now = poly[i];
        if (now == '1' || now == '2' || now == '3' || now == '4' || now == '5' || now == '6' || now == '7' || now == '8' || now == '9' || now == '0')
        {
            lenght++;
        }
        else if (i == poly.length() - 1)
        {
            break;
        }
        else
        {
            break;
        }
    }
    return lenght;
}

float Result(vector<float> num, vector<char> sign)
{
    float result = 0;
    for (unsigned i = 0; i < num.size(); i++)
    {
        if (sign[i] == '+')
        {
            result += num[i];
        }
        else if (sign[i] == '-')
        {
            result -= num[i];
        }
    }
    return result;
}

void elaborateInput(vector<float> &num, vector<char> &sign, vector<int> &firstPos, vector<int> minus)
{
    for (unsigned i = 0; i < minus.size(); i++)
    {
        int pos = minus[i];
        num[pos] *= -1;
    }
    for (unsigned i = 0; i < firstPos.size(); i++)
    {
        int pos = firstPos[i];
        if (sign[pos] == '*')
            num[pos] *= num[pos - 1];
        else if (sign[pos] == '/')
        {
            num[pos] = num[pos - 1] / num[pos];
        }
        num[pos - 1] = 0;
        sign[pos] = sign[pos - 1];
        sign[pos - 1] = '+';
    }
}

void parHandler(string poly, int &i, vector<float> &num)
{
    double tempNum = 0;
    string parOp;
    vector<float> num1;
    vector<char> sign1;
    vector<int> firstPos1;
    vector<int> minus1;
    i += 2;
    for (int j = i; j < poly.length(); j++)
    {
        if (poly[j] == ')' && (poly[j + 1] != ')' || j == poly.length() - 1))
        {
            i = j;
            break;
        }
        else
        {
            parOp += poly[j];
        }
        i = j;
    }
    input(num1, sign1, firstPos1, minus1, parOp);
    elaborateInput(num1, sign1, firstPos1, minus1);
    tempNum = Result(num1, sign1);
    num.push_back(tempNum);
}

void signHandler(vector<char> &sign, string poly, int &i, vector<int> &firstPos, vector<int> &minus)
{
    char next = poly[i + 1];
    if (!(i == poly.length() - 1))
    {
        sign.push_back(next);
        int posTemp = sign.size() - 1;
        if (next == '*' || next == '/')
        {
            firstPos.push_back(posTemp);
        }
        if (poly[i + 2] == '-')
        {
            int posMinus = sign.size() - 1;
            minus.push_back(posMinus);
            i++;
        }
        i++;
    }
}

void input(vector<float> &num, vector<char> &sign, vector<int> &firstPos, vector<int> &minus, string poly)
{
    for (int i = 0; i < poly.length(); i++)
    {
        char now = poly[i];
        char next = poly[i + 1];
        if (now == '1' || now == '2' || now == '3' || now == '4' || now == '5' || now == '6' || now == '7' || now == '8' || now == '9' || now == '0')
        {
            if (i == 0)
            {
                sign.push_back('+');
            }
            if (next == '+' || next == '-' || next == '*' || next == '/' || i == poly.length() - 1)
            {
                float tempNum = now - '0';
                num.push_back(tempNum);
                signHandler(sign, poly, i, firstPos, minus);
                if (poly[i + 1] == '(')
                {
                    parHandler(poly, i, num);
                }
            }
            else if (next == '.' || next == ',')
            {
                float tempNum = now - '0';
                i += 2;
                float base = 0.1;
                for (int j = i; j < poly.length(); j++)
                {
                    char next1 = poly[j + 1];
                    tempNum += (poly[j] - '0')*base;
                    base /= 10;
                    if (next1 == '+' || next1 == '-' || next1 == '*' || next1 == '/' || j == poly.length() - 1)
                    {
                        num.push_back(tempNum);
                        signHandler(sign, poly, j, firstPos, minus);
                        if (poly[j + 1] == '(')
                        {
                            parHandler(poly, j, num);
                        }
                        break;
                    }
                    i = j;
                }
            }
            else if (next == '1' || next == '2' || next == '3' || next == '4' || next == '5' || next == '6' || next == '7' || next == '8' || next == '9' || next == '0')
            {
                int lenght = numLenght(i, poly);
                float tempNum = 0;
                float base = pow(10, lenght) / 10;
                for (int j = i; j < poly.length(); j++)
                {
                    char next1 = poly[j + 1];
                    tempNum += (poly[j] - '0')*base;
                    base /= 10;
                    if (next1 == '+' || next1 == '-' || next1 == '*' || next1 == '/' || j == poly.length() - 1)
                    {
                        num.push_back(tempNum);
                        signHandler(sign, poly, j, firstPos, minus);
                        if (poly[j + 1] == '(')
                        {
                            parHandler(poly, j, num);
                        }
                        break;
                    }
                    else if (next1 == '.' || next1 == ',')
                    {
                        j++;
                        i++;
                    }
                    i++;
                }
            }
        }
        else if ((now == '+' || now == '-' || now == '*' || now == '/') && poly[i - 1] == ')')
        {
            i--;
            signHandler(sign, poly, i, firstPos, minus);
        }
        else if (now == '(')
        {
            if (i == 0)
            {
                sign.push_back('+');
            }
            i--;
            parHandler(poly, i, num);
        }
    }
}

int main()
{
    vector<float> num;
    vector<char> sign;
    vector<int> firstPos;
    vector<int> minus;
    string poly;
    cout << "Inserisci un'espressione: ";
    cin >> poly;
    input(num, sign, firstPos, minus, poly);
    for (int i = 0; i < sign.size(); i++)
    {
        cout << sign[i] << num[i];
    }
    elaborateInput(num, sign, firstPos, minus);
    cout << "\nIl risultato e " << Result(num, sign) << endl;
    char choose;
    cout << "Altre operazioni(s/n)? ";
    cin >> choose;
    if (choose == 's')
        main();
    return 0;
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Could be done much more concisely with a parser and lexer. \$\endgroup\$ – Martin York Jun 26 '16 at 3:07
2
\$\begingroup\$

Think more about the logic

Right now, your numlenght also has some logic that doesn't seem to me like it makes much sense at all:

    if (i == poly.length() - 1)
    {
        break;
    }
    else
    {
        break;
    }

It appears that the test in the if statement makes no real difference at all--you're going to carry out the same action whether it's true or false. You might as well replace that whole chunk with break; (but before you edit, see the next point below).

Use the standard library

Your numLenght [sic] could be implemented a lot more easily using find_first_not_of:

return poly.find_first_not_of("0123456789");

Names

At least to me, some of you names seem fairly meaningless. For example, I'm not sure what parHandler is supposed to mean.

Conclusion

Ultimately, I agree with Loki Astari this this would be much better handled with a recognizable lexer and parser. Rather than post some code for that, I'll point you to a previous question where both he and I posted our ideas of how to accomplish roughly what you're doing here.

\$\endgroup\$
1
\$\begingroup\$

Naming/Function Prototypes

If you're going to go to the effort to declaring your function prototypes, it's worth adding parameter names so that it's clearer what the expectations are. Looking at the following prototype, do you really have any idea what it does? The only hint is the pretty nondescript name of the method input.

void input(vector<float> &, vector<char> &, vector<int> &, vector<int> &, string);

Repetition

You do this a lot:

if (now == '1' || now == '2' || now == '3' || now == '4' || now == '5' || now == '6' || now == '7' || now == '8' || now == '9' || now == '0')

This could be replaced by:

if(now >= '0' && now <= '9')

And abstracted into a method, or better yet, you could just use isdigit.

You do similar things with signs:

next1 == '+' || next1 == '-' || next1 == '*' || next1 == '/' 

Could be abstracted into an isSign(char) method which would make the code easier to follow.

Recursion

This is odd:

if (choose == 's')
    main();

Typically you'd use some kind of do/while loop if you wanted to reuse the functionality, recursively calling main is odd. If you were using it, you'd also be losing the return value from the recursive main calls.

Structure

Your vectors mostly seem to exist so that you can pass them through from one method to another, main doesn't really need to know the contents (although you're printing it out, this looks like it is more for debugging purposes). I'd consider wrapping the functionality up into a class that took in the string and allowed it to be evaluated into a result. That way if you choose to change the implementation of the parser, it's clients don't need to know how it works under the hood.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.