This is possible in one go. The idea is to look for the previous / next operation around each () block and apply the rules of associativity. Here is a small table with yes / no labels when () is needed.
// (a + b) + c NO // (a + b) - c NO // (a + b) / c YES // (a + b) * c YES // (a / b) + c NO // (a / b) - c NO // (a / b) / c NO // (a / b) * c NO // a + (b + c) NO // a - (b + c) YES // a / (b + c) YES // a * (b + c) YES // a + (b / c) NO // a - (b / c) NO // a / (b / c) YES // a * (b / c) NO // (a) ((a)) NO
Here is the C ++ code (I'm not sure if it is missing in some cases - its just an idea):
string clear(string expression) { std::stack<int> openers; std::stack<int> closers; std::stack<bool> isJustClosed; std::stack<char> prevOperations; std::stack<bool> isComposite; std::stack<int> toDelete; prevOperations.push(' '); isJustClosed.push(false); isComposite.push(false); string result = expression + "@"; for (int i = 0; i < result.length(); i++) { char ch = result[i]; if ((ch == '*') || (ch == '/') || (ch == '+') || (ch == '-') || (ch == '(') || (ch == ')') || (ch == '@')) if (isJustClosed.size() > 0) if (isJustClosed.top() == true) { // pop all and decide! int opener = openers.top(); openers.pop(); int closer = closers.top(); closers.pop(); char prev = prevOperations.top(); prevOperations.pop(); char prevOperationBefore = prevOperations.top(); isJustClosed.pop(); //isJustClosed.push(false); bool isComp = isComposite.top(); isComposite.pop(); bool ok = true; if (prev == ' ') ok = false; else { ok = false; if (((isComp) || (prev == '+') || (prev == '-')) && (ch == '/')) ok = true; if (((isComp) || (prev == '+') || (prev == '-')) && (ch == '*')) ok = true; if (((isComp) || (prev == '+') || (prev == '-')) && (prevOperationBefore == '-')) ok = true; if (prevOperationBefore == '/') ok = true; if (((isComp) || (prev == '+') || (prev == '-')) && (prevOperationBefore == '*')) ok = true; } if (!ok) { toDelete.push(opener); toDelete.push(closer); } } if (ch == '(') { openers.push(i); prevOperations.push(' '); isJustClosed.push(false); isComposite.push(false); } if (ch == ')') { closers.push(i); isJustClosed.top() = true; } if ((ch == '*') || (ch == '/') || (ch == '+') || (ch == '-')) { if (!isComposite.top()) { char prev = prevOperations.top(); if ((ch == '+') || (ch == '-')) if ((prev == '*') || (prev == '/')) isComposite.top() = true; if ((ch == '*') || (ch == '/')) if ((prev == '+') || (prev == '-')) isComposite.top() = true; } prevOperations.top() = ch; isJustClosed.top() = false; } } while (toDelete.size() > 0) { int pos = toDelete.top(); toDelete.pop(); result[pos] = ' '; } result.erase(result.size() - 1, 1); return result; }
Inside each block, we track the last operation, and also track whether the content is composite, like (a + b * c).
Test:
void test() { LOG << clear("((a + (a + b))) - ((c)*(c) + d) * (b + d)") << NL; LOG << clear("a + (a + b) - ((c) + d) * (b + d)") << NL; LOG << clear("(a/b)*(c/d)") << NL; LOG << clear("(a/b)*((((c)/d)))") << NL; LOG << clear("((a + b) - (c - d))") << NL; LOG << clear("((a + b)*((c - d)))+c/d*((a*b))") << NL; LOG << clear("a+a*b*(a/b)") << NL; LOG << clear("a+a*b*(a+b)") << NL; }
Result:
a + a + b - ( c * c + d) * b + da + a + b - ( c + d) * b + da/b * c/da/b * c /da + b - (c - d) (a + b)* c - d +c/d* a*b a+a*b* a/b a+a*b*(a+b)
Victor laskin
source share