Challenge accepted!

// 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 ses­sion where I was chal­lenged to imple­ment some code in a short peri­od of time.  I got flus­tered, and I diverged from my usu­al­ly strict prin­ci­ples of code eti­quette and wrote some messy code that nev­er end­ed up work­ing by the end of the ses­sion.  I had an ele­gant solu­tion in mind, but I was wor­ried about time and end­ed 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 seri­ous­ly ugly code, and it will prob­a­bly nev­er work as it is.  I should have start­ed with a tree struc­ture from the begin­ning, but I was wor­ried that I would­n’t be able to get it right in the 45 min­utes I had to do it.  By com­pro­mis­ing, I end­ed up not com­plet­ing the task at all.

It was an illus­tra­tion in real time of the impor­tance of stick­ing with my pro­gram­ming prin­ci­ples at all times.  I was rush­ing myself and I caused myself unnec­es­sary pain.  I did­n’t com­plete the task, while I might have been suc­cess­ful if I had tak­en 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 exact­ly the same, as I’ve had time to think about my imple­men­ta­tion.  I have made sure to not write any actu­al 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) short­ly after.

Share