Gå til indholdet

N249 Eval i en OOP form (SVÆR)

Dette er en af de sværere opgaver. Du kan vælge at analysere min løsning og evt. prøve at skrive din egen, men vigtigst er at du prøve at gennemtænke en mulig løsning på denne opgave.

Info

Se samme opgave i procedural form her.

Du skal skabe en konsol app som kan evaluere en simpel formel som kan indeholde følgende elementer:

  • Heltal (f.eks. 5)
    • operatoren
  • Alle mellemrum ignoreres

I denne version af opgaven skal du løse den med brug af klasser og forsøge at tænke som en kompiler der både validerer et udtryk ved at parse det i tokens (her tal og en enkelt operator) og derefter evaluere det.

Her er et par eksempler på hvordan det skal fungere (fra min løsning):

var evaluator = new Evaluator();

Console.WriteLine(evaluator.Eval("1+2+3"));                   // Output: 6
Console.WriteLine(evaluator.Eval("10+20"));                   // Output: 30
Console.WriteLine(evaluator.Eval("5"));                       // Output: 5
Console.WriteLine(evaluator.Eval("1+1+1+1+1"));               // Output: 5
Console.WriteLine(evaluator.Eval("1   + 1      +1+  1+1"));   // Output: 5
Console.WriteLine(evaluator.Eval("1+1+1+1+1+1+1+1+1"));       // Output: 9

// Ugyldige inputs
//Console.WriteLine(evaluator.Eval(""));                      // Skal kaste en fejl
//Console.WriteLine(evaluator.Eval("1++2"));                  // Skal kaste en fejl
//Console.WriteLine(evaluator.Eval("+1"));                    // Skal kaste en fejl

Ekstra udfordring

Overvej hvordan andre operatorer kunne implementeres.

Klik for at se et forslag til en løsning
var evaluator = new Evaluator();

Console.WriteLine(evaluator.Eval("1+2+3"));                   // Output: 6
Console.WriteLine(evaluator.Eval("10+20"));                   // Output: 30
Console.WriteLine(evaluator.Eval("5"));                       // Output: 5
Console.WriteLine(evaluator.Eval("1+1+1+1+1"));               // Output: 5
Console.WriteLine(evaluator.Eval("1   + 1      +1+  1+1"));   // Output: 5
Console.WriteLine(evaluator.Eval("1+1+1+1+1+1+1+1+1"));       // Output: 9

// Ugyldige inputs
//Console.WriteLine(evaluator.Eval(""));                      // Skal kaste en fejl
//Console.WriteLine(evaluator.Eval("1++2"));                  // Skal kaste en fejl
//Console.WriteLine(evaluator.Eval("+1"));                    // Skal kaste en fejl

class Evaluator
{
    public int Eval(string expression)
    {
        if (string.IsNullOrWhiteSpace(expression))
            throw new ArgumentException("Missing expression");

        List<Token> tokens = Tokenize(expression);
        return ParseTokens(tokens);
    }

    private List<Token> Tokenize(string expression)
    {
        List<Token> tokens = new List<Token>();
        int index = 0;
        int length = expression.Length;

        while (index < length)
        {
            char currentChar = expression[index];

            if (char.IsWhiteSpace(currentChar))
            {
                // Springer mellemrum over
                index++;
            }
            else if (char.IsDigit(currentChar))
            {
                int number = ParseNumber(expression, ref index);
                tokens.Add(new Token(TokenType.Number, number));
            }
            else if (currentChar == '+')
            {
                tokens.Add(new Token(TokenType.Operator, currentChar));
                index++;
            }
            else
            {
                throw new ArgumentException($"Ugyldigt tegn '{currentChar}' ved position {index}");
            }
        }

        return tokens;
    }

    private int ParseNumber(string expression, ref int index)
    {
        int length = expression.Length;
        int start = index;

        while (index < length && char.IsDigit(expression[index]))
        {
            index++;
        }

        string numberStr = expression.Substring(start, index - start);
        return int.Parse(numberStr);
    }

    private int ParseTokens(List<Token> tokens)
    {
        if (tokens.Count == 0)
            throw new ArgumentException("Udtrykket indeholder ingen tokens.");

        int result = 0;
        bool expectNumber = true;

        for (int i = 0; i < tokens.Count; i++)
        {
            Token token = tokens[i];

            if (expectNumber)
            {
                if (token.Type == TokenType.Number)
                {
                    result += token.NumberValue;
                    expectNumber = false;
                }
                else
                {
                    throw new ArgumentException($"Forventede et tal, men fandt '{token.OperatorValue}' ved position {i}");
                }
            }
            else
            {
                if (token.Type == TokenType.Operator && token.OperatorValue == '+')
                {
                    expectNumber = true;
                }
                else
                {
                    throw new ArgumentException($"Forventede '+', men fandt '{token.OperatorValue}' ved position {i}");
                }
            }
        }

        if (expectNumber)
        {
            throw new ArgumentException("Udtrykket sluttede uventet efter en operator.");
        }

        return result;
    }
}

enum TokenType
{
    Number,
    Operator
}

class Token
{
    public TokenType Type { get; }
    public int NumberValue { get; }
    public char OperatorValue { get; }

    public Token(TokenType type, int numberValue)
    {
        Type = type;
        NumberValue = numberValue;
    }

    public Token(TokenType type, char operatorValue)
    {
        Type = type;
        OperatorValue = operatorValue;
    }
}