From bfafaac319686fd9c5de4cd875e8a04b42977ad2 Mon Sep 17 00:00:00 2001 From: Benjamin Bertrand Date: Sat, 13 Feb 2016 20:54:13 +0300 Subject: [PATCH] rewrite expression.simplify and adapt Fraction to it --- pymath/calculus/expression.py | 164 +++++++++++++++++++--------------- pymath/calculus/fraction.py | 4 +- 2 files changed, 95 insertions(+), 73 deletions(-) diff --git a/pymath/calculus/expression.py b/pymath/calculus/expression.py index e8a0e26..4faff4b 100644 --- a/pymath/calculus/expression.py +++ b/pymath/calculus/expression.py @@ -103,32 +103,34 @@ class Expression(Explicable): ) ) - if len(expression.postfix_tokens) == 1: - token = expression.postfix_tokens[0] + # if len(expression.postfix_tokens) == 1: + # token = expression.postfix_tokens[0] - if isinstance(token, Explicable_int) or isinstance(token, int): - return Explicable_int(token) + # if isinstance(token, Explicable_int) or isinstance(token, int): + # return Explicable_int(token) - # TODO: J'en arrive au soucis même soucis qu'avec les fractions qui une fois simplifiée devrait être des Explicable_int. Mais comment on ne redéfini pas les opérations, ce sont les opérations des int qui se font et donc on perd toute l'historique. |sam. févr. 13 18:57:45 EAT 2016 - if isinstance(token, Explicable_float) or isinstance(token, float): - return Explicable_float(token) + # # TODO: J'en arrive au soucis même soucis qu'avec les fractions qui une fois simplifiée devrait être des Explicable_int. Mais comment on ne redéfini pas les opérations, ce sont les opérations des int qui se font et donc on perd toute l'historique. |sam. févr. 13 18:57:45 EAT 2016 + # if isinstance(token, Explicable_float) or isinstance(token, float): + # return Explicable_float(token) - elif hasattr(token, 'simplify') and hasattr(token, 'explain'): - ans = expression.postfix_tokens[0] - return ans + # elif hasattr(token, 'simplify') and hasattr(token, 'explain'): + # ans = expression.postfix_tokens[0] + # return ans - elif isinstance(token, str): - from .polynom import Polynom - return Polynom([0,1], letter = token) + # elif isinstance(token, str): + # from .polynom import Polynom + # return Polynom([0,1], letter = token) - else: - raise ValueError( - "Unknow token type in Expression: {}".format( - type(token))) + # else: + # raise ValueError( + # "Unknow token type in Expression: {}".format( + # type(token))) - else: - expression._isExpression = 1 - return expression + # else: + # expression._isExpression = 1 + # return expression + expression._isExpression = 1 + return expression def __str__(self): """ @@ -144,43 +146,77 @@ class Expression(Explicable): def simplify(self): """ Compute entirely the expression and return the result with .steps attribute """ - self.compute_exp() + try: + self.compute_exp() + except ComputeError: + try: + self.simplified = self.postfix_tokens[0].simplify() + except AttributeError: + if isinstance(self.postfix_tokens[0],int): + self.simplified = Explicable_int(self.postfix_tokens[0]) + elif isinstance(self.postfix_tokens[0],float): + self.simplified = Explicable_float(self.postfix_tokens[0]) + else: + self.simplified = self - self.simplified = self.child.simplify() - self.simplified.steps = self.child.steps + self.simplified.steps + else: + self.simplified = self.child.simplify() + self.simplified.steps = self.child.steps + self.simplified.steps return self.simplified def compute_exp(self): """ Create self.child with and stock steps in it """ - ini_step = Expression(self.postfix_tokens) + if len(self.postfix_tokens) == 1: + raise ComputeError("Nothing to compute in {}".format(self.postfix_tokens)) + else: + ini_step = Expression(self.postfix_tokens) - tokenList = self.postfix_tokens.copy() - tmpTokenList = [] + tokenList = self.postfix_tokens.copy() + tmpTokenList = [] - while len(tokenList) > 2: - # on va chercher les motifs du genre A B +, quand l'operateur est - # d'arité 2, pour les calculer - if isNumerand(tokenList[0]) and isNumerand(tokenList[1]) \ - and isOperator(tokenList[2]) and tokenList[2].arity == 2: + while len(tokenList) > 2: + # on va chercher les motifs du genre A B +, quand l'operateur est + # d'arité 2, pour les calculer + if isNumerand(tokenList[0]) and isNumerand(tokenList[1]) \ + and isOperator(tokenList[2]) and tokenList[2].arity == 2: - # S'il y a une opération à faire - op1 = tokenList[0] - op2 = tokenList[1] - operator = tokenList[2] + # S'il y a une opération à faire + op1 = tokenList[0] + op2 = tokenList[1] + operator = tokenList[2] - res = operator(op1, op2) + res = operator(op1, op2) - tmpTokenList.append(res) + tmpTokenList.append(res) - # Comme on vient de faire le calcul, on peut détruire aussi les - # deux prochains termes - del tokenList[0:3] + # Comme on vient de faire le calcul, on peut détruire aussi les + # deux prochains termes + del tokenList[0:3] - # Et les motifs du gens A -, quand l'operateur est d'arité 1 - elif isNumerand(tokenList[0]) \ + # Et les motifs du gens A -, quand l'operateur est d'arité 1 + elif isNumerand(tokenList[0]) \ + and isOperator(tokenList[1]) and tokenList[1].arity == 1: + + # S'il y a une opération à faire + op1 = tokenList[0] + operator = tokenList[1] + + res = operator(op1) + + tmpTokenList.append(res) + + # Comme on vient de faire le calcul, on peut détruire aussi les + # deux prochains termes + del tokenList[0:2] + + else: + tmpTokenList.append(tokenList[0]) + + del tokenList[0] + + if len(tokenList) == 2 and isNumerand(tokenList[0]) \ and isOperator(tokenList[1]) and tokenList[1].arity == 1: - - # S'il y a une opération à faire + # S'il reste deux éléments dont un operation d'arité 1 op1 = tokenList[0] operator = tokenList[1] @@ -192,34 +228,15 @@ class Expression(Explicable): # deux prochains termes del tokenList[0:2] + tmpTokenList += tokenList + self.child = Expression(tmpTokenList) + + steps = self.develop_steps(tmpTokenList) + + if self.child.postfix_tokens == ini_step.postfix_tokens: + self.child.steps = steps else: - tmpTokenList.append(tokenList[0]) - - del tokenList[0] - - if len(tokenList) == 2 and isNumerand(tokenList[0]) \ - and isOperator(tokenList[1]) and tokenList[1].arity == 1: - # S'il reste deux éléments dont un operation d'arité 1 - op1 = tokenList[0] - operator = tokenList[1] - - res = operator(op1) - - tmpTokenList.append(res) - - # Comme on vient de faire le calcul, on peut détruire aussi les - # deux prochains termes - del tokenList[0:2] - - tmpTokenList += tokenList - self.child = Expression(tmpTokenList) - - steps = self.develop_steps(tmpTokenList) - - if self.child.postfix_tokens == ini_step.postfix_tokens: - self.child.steps = steps - else: - self.child.steps = [ini_step] + steps + self.child.steps = [ini_step] + steps def develop_steps(self, tokenList): """ From a list of tokens, it develops steps of each tokens """ @@ -363,6 +380,11 @@ class Expression(Explicable): def __neg__(self): return Expression(self.postfix_tokens + [op.sub1]) +class ExpressionError(Exception): + pass + +class ComputeError(Exception): + pass def untest(exp): a = Expression(exp) diff --git a/pymath/calculus/fraction.py b/pymath/calculus/fraction.py index 5c9254d..67fd93d 100644 --- a/pymath/calculus/fraction.py +++ b/pymath/calculus/fraction.py @@ -60,7 +60,7 @@ class Fraction(Explicable): ini_step = [Expression(self.postfix_tokens)] if self._num == 0: - return Expression([0]) + return Expression([0]).simplify() elif isinstance(self._num, Fraction) or isinstance(self._denom, Fraction): return self._num / self._denom @@ -74,7 +74,7 @@ class Fraction(Explicable): gcd_ = gcd(abs(self._num), abs(self._denom)) if gcd_ == self._denom: n_frac = self._num // gcd_ - return Expression([n_frac]) + return Expression([n_frac]).simplify() elif gcd_ != 1: n_frac = Fraction(self._num // gcd_, self._denom // gcd_)