// You are building an educational website and want to create a simple
// calculator for students to use. The calculator will only allow
// addition and subtraction of positive integers.
//
// We also want to allow parentheses in our input. Given an expression
// string using the "+", "-", "(", and ")" operators like "5+(16-2)",
// write a function to parse the string and evaluate the result.
// Sample input:
// expression1 = "5+16-((9-6)-(4-2))"
// expression2 = "22+(2-4)"
// Sample output:
// evaluate(expression1) => 20
// evaluate(expression2) => 20
Recently, I did a live session where I was challenged to implement some code in a short period of time. I got flustered, and I diverged from my usually strict principles of code etiquette and wrote some messy code that never ended up working by the end of the session. I had an elegant solution in mind, but I was worried about time and ended up with this mess.
Nothing to see here…
using System;
class Solution
{
static int findOperator(string expression) {
int plusPos = expression.IndexOf('+');
int minusPos = expression.IndexOf('-');
int parenPos = expression.IndexOf('(');
int closeParen = expression.IndexOf(')');
plusPos = (plusPos < 0) ? 999 : plusPos;
minusPos = (minusPos < 0) ? 999 : minusPos;
parenPos = (parenPos < 0) ? 999 : parenPos;
closeParen = (closeParen <0) ? 999 : closeParen;
return Math.Min(Math.Min(plusPos, Math.Min(minusPos, parenPos)), closeParen);
}
static (int, int) do_evaluate(string expression) {
int opPos = findOperator(expression);
int r;
if (opPos < 0)
return (0, -1);
System.Console.WriteLine(expression[opPos]);
if (expression[opPos] == '(') {
System.Console.WriteLine("Recursing...");
while (opPos < expression.Length) {
(r, opPos) = do_evaluate(expression.Substring(opPos+1));
opPos = findOperator(expression.Substring(opPos+1));
}
}
int result = int.Parse(expression.Substring(0, opPos));
System.Console.WriteLine(result);
if (expression[opPos] == ')') {
return (result, opPos + 1);
}
var after = expression.Substring(opPos + 1);
(r, _) = do_evaluate(after);
switch (expression[opPos]) {
case '-':
result -= r;
break;
case '+':
default:
result += r;
break;
}
System.Console.WriteLine(result);
return (result, expression.Length);
}
static int evaluate(string expression) {
var (result, _) = do_evaluate(expression);
return result;
}
static void Main(string[] args)
{
string expression1 = "5+16-((9-6)-(4-2))";
string expression2 = "22+(2-4)";
System.Console.WriteLine(evaluate(expression1));
// System.Console.WriteLine(evaluate(expression2));
// System.Console.WriteLine(evaluate("2"));
// System.Console.WriteLine(evaluate("2-2-1"));
// System.Console.WriteLine(evaluate("3+3+3"));
// System.Console.WriteLine(evaluate(""));
}
}
What was I thinking?
This is some seriously ugly code, and it will probably never work as it is. I should have started with a tree structure from the beginning, but I was worried that I wouldn’t be able to get it right in the 45 minutes I had to do it. By compromising, I ended up not completing the task at all.
It was an illustration in real time of the importance of sticking with my programming principles at all times. I was rushing myself and I caused myself unnecessary pain. I didn’t complete the task, while I might have been successful if I had taken my time in the beginning.
To redeem myself in my own eyes, I am going to set a timer and start from scratch. Of course, it’s not exactly the same, as I’ve had time to think about my implementation. I have made sure to not write any actual code until the timer starts, and I have tried not to think about it too much.
Plan to post the results (and maybe a video) shortly after.