Calculatrice Java simple pour résoudre des Expressions


Je travaille sur une calculatrice qui résout les expressions. J'essaie de comprendre comment le faire calculer dans l'ordre PEMDAS. J'ai une boucle for pour parcourir la liste des tableaux et un commutateur qui appelle une classe qui fait le calcul. J'ai essayé les déclarations if, mais je n'ai pas pu le comprendre.

Comment puis-je changer cela pour m'assurer que l'expression est résolue dans le bon ordre? Voici ce que j'ai jusqu'à présent:

/*
Peter Harmazinski
Simple Calculator

This program solves expressions
*/

import java.util.*;

public class SimpleCalculator2 {
    static SimpleMath math = new SimpleMath();

    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        boolean again = true;
        double number1 = 0.0;
        double number2 = 0.0;
        double answer = 0.0;
        double results = 0.0;
        String delims = "[ ]+";

        getIntroduction();

        while (again) {
            System.out.println("Please enter your expression.");
            String input = console.nextLine();
            System.out.println("This is the user's input: " + input);

            //Parses string into array list
            List<String> list = new ArrayList<String>(Arrays.asList(input.split(delims)));
            System.out.println("list: " + list);

            results = doMath(list, number1, number2);
            getResults(results);
        }
        console.close();
    }

    public static void getIntroduction() {
        System.out.println("This is a simple calculator that solves expressions.");
    }

    //Traverses array list to identify operators and does math for surrounding numbers
    //then answer is inserted in i-1 element and the elements i and i+1 are deleted

    public static double doMath(List<String> list, double number1, double number2) {
        double answer = 0.0;
        double results = 0.0;
        while (list.size() > 1) {
            for (int i = 0; i < list.size(); i++) {
                    switch (list.get(i)) {
                        case "*" :
                            number1 = Double.parseDouble(list.get(i - 1));
                            number2 = Double.parseDouble(list.get(i + 1));
                            answer = math.multiply(number1, number2);
                            System.out.println(answer);
                            list.add(i - 1, Double.toString(answer));
                            list.subList(i, i + 3).clear();
                            System.out.println(list);
                            break;
                        case "/" :
                            number1 = Double.parseDouble(list.get(i - 1));
                            number2 = Double.parseDouble(list.get(i + 1));
                            answer = math.divide(number1, number2);
                            System.out.println(answer);
                            list.add(i - 1, Double.toString(answer));
                            list.subList(i, i + 3).clear();
                            System.out.println(list);
                            break;
                        case "+" :
                            number1 = Double.parseDouble(list.get(i - 1));
                            number2 = Double.parseDouble(list.get(i + 1));
                            answer = math.add(number1, number2);
                            System.out.println(answer);
                            list.add(i - 1, Double.toString(answer));
                            list.subList(i, i + 3).clear();
                            System.out.println(list);
                            break;
                        case "-" :
                            number1 = Double.parseDouble(list.get(i - 1));
                            number2 = Double.parseDouble(list.get(i + 1));
                            answer = math.subtract(number1, number2);
                            System.out.println(answer);
                            list.add(i - 1, Double.toString(answer));
                            list.subList(i, i + 3).clear();
                            System.out.println(list);
                            break;
                        }
                }   
            }   
        return answer;
    }

    public static void getResults(double results) {
        System.out.println("Results are: " + results);
    }
}
Author: Peter, 2015-05-21

3 answers

Je pense qu'un algorithme assez standard pour y parvenir est l'algorithme de triage de Dijkstra suivi d'une évaluation postfix. Vous pouvez lire à ce sujet ici, et le pseudocode est ici, mais vous devrez peut-être comprendre les structures de données de base: http://en.wikipedia.org/wiki/Shunting-yard_algorithm .

Si vous ne connaissez pas la notation de la pile, de la file d'attente et du postfix, vous pouvez écrire une implémentation plus lente, simple, mais plus messie. J'ai essayé une fois avec sin, cos, trig, log, ainsi, et le code s'est avéré fonctionnel, mais pas quelque chose que je réessayerais.

Fondamentalement, l'idée est de trouver l'expression la plus prioritaire avec seulement 1 opérateur, de l'évaluer et de la remplacer par elle. Voici quelques pseudocodes:

input = [user input]
while expression still contains (, ), +, -, *, or /:
    toEvaluate = highest priority expression with 1 operator (e.g. 1+2, or 2*3)
    calculate the decimal value of toEvaluate
    modify input so that you replace toEvaluate with its decimal value

Notez que dans votre implémentation doMath(), dans votre boucle for, vous évaluez simplement tous les opérateurs dès que vous les voyez. Par exemple, considérons

1+2*3

Vous verrez + d'abord, calculez 1 + 2 puis multipliez cela résultat par 3. Au lieu de cela, vous devez d'abord parcourir toute la liste, trouver l'opérateur le plus prioritaire, l'évaluer, puis recommencer depuis le début.

 1
Author: user2570465, 2015-05-21 01:37:47

Puisque vous l'entrez dans une chaîne, pourquoi ne pas créer une boucle for qui parcourt chaque caractère?

// This for loop will get you the string with only one operator in it.
// EX: 30+32
// You will have to find some way to compute this string.
for(int i = 0; i < userInput.length(); i++)
{
   //int a will be the INDEX of the first operator
   //int b will be the INDEX of the place where a new operator comes up 
   char c = userInput.getCharAt(i);
   a = 0;
   //I'm just gonna do it with + and - for now. Add any other operators.
   if(c == '+' || c == '-')
   {
       b = i;
       String stringToCompute = userInput.substring(a,b);
       //Find some way to take the string there then compute it. 
       //Maybe another for loop with stringToCompute to find where the
       // operator is and then add/subtract the two doubles.  

       // Now reset it 
       a = i;
       b = null;
   }
}
 -1
Author: Raheel138, 2015-05-21 01:38:13

Voici quelques conceptions possibles pour résoudre ce problème:

  1. Vous pourriez diviser le traitement en deux parties: l'analyse et l'évaluation. Dans la phase d'analyse, vous convertissez les chaînes en une structure de données représentant la façon dont elles seront évaluées. Dans la phase d'évaluation, vous parcourez l'arbre et évaluez l'expression.

  2. Vous pouvez diviser la liste par les opérateurs pour évaluer le dernier (plus et moins), évaluer chacun des segments (fois et diviser). Cela pourrait être récursif si bien conçu.

La première option est meilleure car elle est plus extensible mais c'est aussi plus d'efforts à mettre en œuvre. Voici un exemple de structure de données à générer via l'analyse et l'évaluation:

interface Term {
    double getValue();
}

enum Operator {
    MULTIPLY, DIVIDE, ADD, SUBTRACT;
    double getValue(List<Double> operands) {
        ...
    }
}

class Operation implements Term {
    List<Term> operands;
    Operator operator;
    double getValue() {
        return operator.getValue(operands);
    }
}

class Constant implements Term {
    private final double value;
    double getValue() {
        return value;
    }
}
 -1
Author: sprinter, 2015-05-21 01:43:46