Calculatrice Rpn

Résolu/Fermé
etbienwi Messages postés 18 Date d'inscription lundi 16 octobre 2017 Statut Membre Dernière intervention 22 mai 2018 - Modifié le 5 nov. 2017 à 12:42
etbienwi Messages postés 18 Date d'inscription lundi 16 octobre 2017 Statut Membre Dernière intervention 22 mai 2018 - 7 nov. 2017 à 22:28
Bonjour,
Je travaille sur une calculatrice Rpn.
J'ai une énumération Opérations qui contient les différentes opérations et la méthode de calcul associé.
package calculatrice;

 public enum Operations{
	 Plus("+"){

		@Override
		public double eval(double x, double y) {
				 return x + y;
		}	 
	 },
	 MOINS("-"){
		@Override
		public double eval(double x, double y) {
			return x - y;
		}
},
	 MULT("*"){

		@Override
		public double eval(double x, double y) {
			
			return x*y ;
		}
}, 
	DIV("/"){

		@Override
		public double eval(double x, double y) {
			
			return x/y;
		}
};
				
private String symbole;
	 
private  Operations(String s){
		 this.symbole = s;
	 }
	 	 public abstract double eval(double x, double y);
 
 };

Je dois faire une classe Moteur qui permet d’enregistrer les opérandes et d’effectuer le calcul.
J'ai utilisé une pile pour ranger les opérandes mais je suis perdu pour le calcul en utilisant l'énumération.

package calculatrice;

import java.util.*;

public class MoteurRPN  {
	Stack<Double> p = new Stack<Double> ();
	
	public void enregistrer(Double v) {
		p.push(v);
	}
	
	public void calcul() {
		Operations op = Operations.Plus;
		if (op == Operations.Plus) {
			Double v = p.pop();
			Double y = p.pop();
			double result ;
			result = Operations.Plus.eval(v, y);
			p.push(result);
			
		}
			
		
		}
	}

Voila mon essai pour l'addition, est ce possible de faire comme cela?Je ne sais pas trop comment je suis sensé gérer les chiffres et opérateurs.

Merci d'avance.

1 réponse

KX Messages postés 16752 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 août 2024 3 019
5 nov. 2017 à 13:24
Bonjour,

Je trouve maladroit de faire une méthode abstract dans l'enum et faire autant de classes concrètes que de valeurs de l'enum.

Il y a des alternatives plus intéressantes.

package calculatrice;

public enum Operations {
    PLUS("+"), MOINS("-"), MULT("*"), DIV("/");

    private String symbole;

    private Operations(String s) {
        symbole = s;
    }

    public final double eval(double x, double y) {
        switch (this) {
        case PLUS:
            return x + y;
        case MOINS:
            return x - y;
        case MULT:
            return x * y;
        case DIV:
            return x / y;
        default:
            throw new IllegalStateException("eval(double,double) is not implemented for " + this);
        }
    }
};

Ou en Java 8 ou 9 :

package calculatrice;

import java.util.function.BiFunction;

public enum Operations {
    PLUS("+", (x, y) -> x + y),
    MOINS("-", (x, y) -> x - y),
    MULT("*", (x, y) -> x * y),
    DIV("/", (x, y) -> x / y);

    private final String symbole;
    private final BiFunction<Double, Double, Double> function;

    private Operations(String s, BiFunction<Double, Double, Double> function) {
        symbole = s;
        this.function = function;
    }

    public double eval(double x, double y) {
        return function.apply(x, y);
    }
};

Quant à ton moteur, tu ne peux pas stocker que les opérandes dans ta stack, il faut aussi stocker les opérateurs.

Exemple :
MOINS PLUS 3 4 1
qui donne
Moins(Plus(3,4),1)
c'est à dire (3+4)-1

Pour faire ça il faudrait que tu ais une interface (par exemple Token) partagée entre ton enum Operations (qu'il serait sage de renommer Operator) et une classe Operand, et que dans ta pile tu ne manipules que des Token (alternativement des Operator et ses Operand)

Exemple :

public enum TokenType {
    OPERAND, OPERATOR
}

public interface Token {
    TokenType getType();
}

public class Operand implements Token {
    private final double value;

    public Operand(double value) {
        this.value = value;
    }

    public double getValue() {
        return value;
    }

    @Override
    public TokenType getType() {
        return TokenType.OPERAND;
    }
}

public enum Operator implements Token {
    PLUS((x, y) -> x + y),
    MOINS((x, y) -> x - y),
    MULT((x, y) -> x * y),
    DIV((x, y) -> x / y);

    private final BiFunction<Double, Double, Double> function;

    private Operator(BiFunction<Double, Double, Double> function) {
        this.function = function;
    }

    public Operand eval(Operand x, Operand y) {
        return new Operand(function.apply(x.getValue(), y.getValue()));
    }

    @Override
    public TokenType getType() {
        return TokenType.OPERATOR;
    }
};

public static Operand nextOperand(Stack<Token> tokens) {
    Token token = tokens.pop();
    switch (token.getType()) {
    case OPERAND:
        return (Operand) token;
    case OPERATOR:
        Operand op1 = nextOperand(tokens);
        Operand op2 = nextOperand(tokens);
        return ((Operator) token).eval(op1, op2);
    default:
        throw new IllegalStateException("Unknown " + token.getType());
    }
}

public static void main(String[] args) {
    Stack<Token> tokens = new Stack<Token>();
    tokens.push(new Operand(1));
    tokens.push(new Operand(4));
    tokens.push(new Operand(3));
    tokens.push(Operator.PLUS);
    tokens.push(Operator.MOINS);
    System.out.println(nextOperand(tokens).getValue()); // 6.0
}
0
etbienwi Messages postés 18 Date d'inscription lundi 16 octobre 2017 Statut Membre Dernière intervention 22 mai 2018
6 nov. 2017 à 11:05
Merci beaucoup pour ta réponse.
Je ne savais pas comment mettre dans la pile les opérandes et les opérateurs, c'est la première fois que j'utilise des énumérations. Par contre je ne comprend pas bien ces deux éléments:

private Operator(BiFunction<Double, Double, Double> function) {
this.function = function;
}

public Operand eval(Operand x, Operand y) {
return new Operand(function.apply(x.getValue(), y.getValue()));
}

Je vois bien ce qu'ils permettent de faire mais je ne serai pas capable de refaire un truc semblable(c'est un peu flou...)
J'ai essayé de faire une méthode pour afficher toutes mes opérandes:

public static void contenu(Stack<Token> t) {
while(!t.empty()) {
System.out.println("voila" +t.pop()); }

}

elle affiche les opérators mais pour les chiffres elle m'affiche :
calculatrice.Operand@6ce253f1


Merci d'avance.
0
KX Messages postés 16752 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 août 2024 3 019
6 nov. 2017 à 13:18
"elle affiche les opérators mais pour les chiffres elle m'affiche calculatrice.Operand@6ce253f1"
C'est parce que la méthode toString n'est pas redéfini dans la classe Operand (alors qu'elle l'est dans les enum).

@Override
public String toString() {
    return String.valueOf(value);
}


Remarque : tu peux faire
System.out.println(t);
directement, ça marche aussi, et ça évite de faire un pop() qui va supprimer les valeurs de ta pile...

"je ne comprend pas bien ces deux éléments"
BiFunction<T,U,R>
est une interface qui possède une méthode
R apply(T,U)
ce qui correspond bien à ce que tu souhaites faire avec tes opérateurs.

À voir : java.util.function.BiFunction<T,U,R>

Comme c'est une interface qui n'a qu'une seule méthode à implémenter on dit que c'est une interface fonctionnelle, ce qui permet d'utiliser des lambda comme
(x,y) -> x+y;
mais on pourrait également faire comme ceci, ça passerait à la compilation, ça reviendrait à ton code de départ, mais c'est moins performant :

PLUS(new BiFunction<Double,Double,Double>() {
    @Override
    public Double apply(Double x, Double y) {
        return x+y;
    }
})

NB. On pourrait également faire des références de méthodes.

À voir : Java - Les Structures fonctionnelles

Dans la suite, le
return new Operand(function.apply(x.getValue(), y.getValue()));
est une application directe de cette fonction, il faut juste faire la conversion entre les types Double et Operand.

Remarque : ce n'est pas forcément utile ici, mais si on voulait être bien propre, je ferai implémenter la classe Operator de l'interface BiFunction, il faudrait juste renommer la méthode eval(x,y) en apply(x,y).

import java.util.function.BiFunction;

public enum Operator implements Token, BiFunction<Operand, Operand, Operand> {
    PLUS((x, y) -> x + y),
    MOINS((x, y) -> x - y),
    MULT((x, y) -> x * y),
    DIV((x, y) -> x / y);

    private final BiFunction<Double, Double, Double> function;

    private Operator(BiFunction<Double, Double, Double> function) {
        this.function = function;
    }

    @Override
    public Operand apply(Operand x, Operand y) {
        return new Operand(function.apply(x.getValue(), y.getValue()));
    }

    @Override
    public TokenType getType() {
        return TokenType.OPERATOR;
    }
};
0
etbienwi Messages postés 18 Date d'inscription lundi 16 octobre 2017 Statut Membre Dernière intervention 22 mai 2018 > KX Messages postés 16752 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 août 2024
6 nov. 2017 à 23:38
Bonjour,
Merci beaucoup, avec les exemples j'ai bien compris maintenant comme cela fonctionne( Grace aux liens je vais pouvoir approfondir ).
Je veux essayer maintenant d'utiliser la classe scanner de java.util pour entrer mes opérandes et faire le calcul. J'ai fait une autre classe qui est sensé utiliser la classe opérande. Cette dernière fait déjà tout le travail et je ne sais pas comment les lier.
J'ai obtenu cela:

public void lancement() {
Stack<Token> tokens = new Stack<Token>();
System.out.println("Veuillez saisir l'opérande");
Double str = sc.nextDouble();
new Operand(str = sc.nextDouble());
tokens.push (new Operand(str));

}


Est ce c'est de cette manière que l'on est sensé le faire?
Merci d'avance.
0
KX Messages postés 16752 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 août 2024 3 019 > etbienwi Messages postés 18 Date d'inscription lundi 16 octobre 2017 Statut Membre Dernière intervention 22 mai 2018
Modifié le 7 nov. 2017 à 00:00
La ligne
new Operand(str = sc.nextDouble());
ne sert à rien, mais sinon le reste c'est ça... ceci dit c'est assez limité de partir du principe que l'on a forcément une opérande alors qu'on devrait aussi pouvoir saisir un opérateur.
0
etbienwi Messages postés 18 Date d'inscription lundi 16 octobre 2017 Statut Membre Dernière intervention 22 mai 2018 > KX Messages postés 16752 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 août 2024
7 nov. 2017 à 15:26
J'ai essaye de configurer la saisie pour qu'on puisse mettre des opérandes et des opérateurs.
J'ai voulu utiliser la même méthode que pour le nextOperand mais je ne m'en sors pas vraiment.

public static Double lancement() {
Stack<Token> tokens = new Stack<Token>();
double d;
System.out.println("combien de parametre aller vous rentrer");
int nbr = sc.nextInt();
for(int i =1; i<=nbr; i++) {
System.out.println("Veuillez saisir l'opérande");
String str = sc.next();
Token token =( ?);
switch (token.getType()) {
case OPERAND:
d = Double.parseDouble (str);
tokens.push (new Operand(d));
case Operator:
tokens.push( Operator.valueOf(str));
break;
}
}
double h = cacul(tokens);
return h;

comment faire pour que ce qui rentre en paramètre puisse être considéré comme une Opérande ou un Opérateur.
0