TP : Arithmétique simple¶
Oublions temporairement nos utilisateurs et notre forum, et intéressons-nous à l’évaluation mathématique.
Imaginons que nous voulions représenter une expression mathématique, qui pourrait contenir des termes variables (par exemple, 2 * (-x + 1)).
Il va nous falloir utiliser un type pour représenter cette variable x, appelé Var, et un second pour l’expression non évaluée, Expr.
Les Var étant un type particulier d’expressions.
Nous aurons deux autres types d’expressions : les opérations arithmétiques unaires (+, -) et binaires (+, -, *, /, //, %, **).
Vous pouvez vous appuyer un même type pour ces deux types d’opérations.
L’expression précédente s’évaluerait par exemple à :
BinOp(operator.mul, 2, BinOp(operator.add, UnOp(operator.neg, Var('x')), 1))
Nous ajouterons à notre type Expr une méthode compute(**values), qui permettra de calculer l’expression suivant une valeur donnée, de façon à ce que Var('x').compute(x=5) retourne 5.
Enfin, nous pourrons ajouter une méthode __repr__ pour obtenir une représentation lisible de notre expression.
import operator
def compute(expr, **values):
if not isinstance(expr, Expr):
return expr
return expr.compute(**values)
class Expr:
def compute(self, **values):
raise NotImplementedError
def __pos__(self):
return UnOp(operator.pos, self, '+')
def __neg__(self):
return UnOp(operator.neg, self, '-')
def __add__(self, rhs):
return BinOp(operator.add, self, rhs, '+')
def __radd__(self, lhs):
return BinOp(operator.add, lhs, self, '+')
def __sub__(self, rhs):
return BinOp(operator.sub, self, rhs, '-')
def __rsub__(self, lhs):
return BinOp(operator.sub, lhs, self, '-')
def __mul__(self, rhs):
return BinOp(operator.mul, self, rhs, '*')
def __rmul__(self, lhs):
return BinOp(operator.mul, lhs, self, '*')
def __truediv__(self, rhs):
return BinOp(operator.truediv, self, rhs, '/')
def __rtruediv__(self, lhs):
return BinOp(operator.truediv, lhs, self, '/')
def __floordiv__(self, rhs):
return BinOp(operator.floordiv, self, rhs, '//')
def __rfloordiv__(self, lhs):
return BinOp(operator.floordiv, lhs, self, '//')
def __mod__(self, rhs):
return BinOp(operator.mod, self, rhs, '*')
def __rmod__(self, lhs):
return BinOp(operator.mod, lhs, self, '*')
class Var(Expr):
def __init__(self, name):
self.name = name
def compute(self, **values):
if self.name in values:
return values[self.name]
return self
def __repr__(self):
return self.name
class Op(Expr):
def __init__(self, op, *args):
self.op = op
self.args = args
def compute(self, **values):
args = [compute(arg, **values) for arg in self.args]
return self.op(*args)
class UnOp(Op):
def __init__(self, op, expr, symbol=None):
super().__init__(op, expr)
self.symbol = symbol
def __repr__(self):
if self.symbol is None:
return super().__repr__()
return '{}{!r}'.format(self.symbol, self.args[0])
class BinOp(Op):
def __init__(self, op, expr1, expr2, symbol=None):
super().__init__(op, expr1, expr2)
self.symbol = symbol
def __repr__(self):
if self.symbol is None:
return super().__repr__()
return '({!r} {} {!r})'.format(self.args[0], self.symbol, self.args[1])
if __name__ == '__main__':
x = Var('x')
expr = 2 * (-x + 1)
print(expr)
print(compute(expr, x=1))
y = Var('y')
expr += y
print(compute(expr, x=0, y=10))