Merge branch 'calc_fraction_nostr' into calc_fraction

Conflicts:
	calculus.py
	fraction.py
This commit is contained in:
Lafrite 2013-10-29 11:52:09 +01:00
commit f4ea0dcc42
2 changed files with 158 additions and 102 deletions

View File

@ -3,32 +3,51 @@
from generic import Stack from generic import Stack
from fraction import Fraction
def str2tokens(exp):
"""Convert an expression into a list of tokens
:param exp: The expression
:returns: the list of tokens
def infixToPostfix(infixExp): >>> str2tokens("1 + 2")
"""Transform an infix expression into postfix expression [1, '+', 2]
:param infixExp: an infix expression (caracters must be separate by a space) """
:returns: the corresponding postfix expression tokens = exp.split(" ")
for (i,t) in enumerate(tokens):
try:
tokens[i] = int(t)
except ValueError:
pass
return tokens
def infixToPostfix(infixTokens):
"""Transform an infix list of tokens into postfix tokens
:param infixTokens: an infix list of tokens
:returns: the corresponding postfix list of tokens
:Example: :Example:
>>> infixToPostfix("1 + 2") >>> infixToPostfix([1, "+", 2])
'1 2 +' [1, 2, '+']
>>> infixToPostfix("1 * 2 + 3")
'1 2 * 3 +'
>>> infixToPostfix([1, "*", 2, "+", 3])
[1, 2, '*', 3, '+']
""" """
priority = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} priority = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1}
opStack = Stack() opStack = Stack()
postfixList = [] postfixList = []
tokenList = infixExp.split(" ") #infixTokens = infixExp.split(" ")
for token in tokenList: for token in infixTokens:
if token == "(": if token == "(":
opStack.push(token) opStack.push(token)
elif token == ")": elif token == ")":
@ -36,7 +55,8 @@ def infixToPostfix(infixExp):
while topToken != "(": while topToken != "(":
postfixList.append(topToken) postfixList.append(topToken)
topToken = opStack.pop() topToken = opStack.pop()
elif token in "+-*/": elif isOperation(token):
# On doit ajouter la condition == str sinon python ne veut pas tester l'appartenance à la chaine de caractère.
while (not opStack.isEmpty()) and (priority[opStack.peek()] >= priority[token]): while (not opStack.isEmpty()) and (priority[opStack.peek()] >= priority[token]):
postfixList.append(opStack.pop()) postfixList.append(opStack.pop())
opStack.push(token) opStack.push(token)
@ -46,23 +66,23 @@ def infixToPostfix(infixExp):
while not opStack.isEmpty(): while not opStack.isEmpty():
postfixList.append(opStack.pop()) postfixList.append(opStack.pop())
return " ".join(postfixList) return postfixList
def computePostfix(postfixExp): def computePostfix(postfixTokens):
"""Compute a postfix expression """Compute a postfix list of tokens
:param postfixExp: a postfix expression :param postfixTokens: a postfix list of tokens
:returns: the result of the expression :returns: the result of the calculus
""" """
print(postfixToInfix(postfixExp)) #print(postfixToInfix(postfixExp))
# where to save numbers or # where to save numbers or
operandeStack = Stack() operandeStack = Stack()
tokenList = postfixExp.split(" ") #tokenList = postfixExp.split(" ")
for (i,token) in enumerate(tokenList): for (i,token) in enumerate(postfixTokens):
if token in "+-*/": if isOperation(token):
op2 = operandeStack.pop() op2 = operandeStack.pop()
op1 = operandeStack.pop() op1 = operandeStack.pop()
res = doMath(token, op1, op2) res = doMath(token, op1, op2)
@ -70,28 +90,29 @@ def computePostfix(postfixExp):
#print("Operation: {op1} {op} {op2}".format(op1 = op1, op = token, op2 = op2)) #print("Operation: {op1} {op} {op2}".format(op1 = op1, op = token, op2 = op2))
#print(operandeStack) #print(operandeStack)
#print(tokenList[i+1:]) #print(postfixTokens[i+1:])
newPostfix = " ".join(operandeStack + tokenList[i+1:]) newPostfix = " ".join(operandeStack + postfixTokens[i+1:])
print(postfixToInfix(newPostfix)) #print(postfixToInfix(newPostfix))
else: else:
operandeStack.push(token) operandeStack.push(token)
return operandeStack.pop() return operandeStack.pop()
def computePostfixBis(postfixExp): def computePostfixBis(postfixTokens):
"""Compute a postfix expression like a good student """Compute a postfix list of tokens like a good student
:param postfixExp: a postfix expression :param postfixTokens: a postfix list of tokens
:returns: the result of the expression :returns: the result of the expression
""" """
print(postfixExp)
print(postfixToInfix(postfixExp))
# where to save numbers or # where to save numbers or
operandeStack = Stack() operandeStack = Stack()
tokenList = postfixExp.split(" ") #tokenList = postfixExp.split(" ")
tokenList = postfixTokens.copy()
steps = []
steps = [] steps = []
@ -100,11 +121,12 @@ def computePostfixBis(postfixExp):
tmpTokenList = [] tmpTokenList = []
# on va chercher les motifs du genre A B + pour les calculer # on va chercher les motifs du genre A B + pour les calculer
while len(tokenList) > 2: while len(tokenList) > 2:
if isNumber(tokenList[0]) and isNumber(tokenList[1]) and tokenList[2] in "+-*/": if isNumber(tokenList[0]) and isNumber(tokenList[1]) and isOperation(tokenList[2]):
# S'il y a une opération à faire # S'il y a une opération à faire
op1 = tokenList[0] op1 = tokenList[0]
op2 = tokenList[1] op2 = tokenList[1]
token = tokenList[2] token = tokenList[2]
res = doMath(token, op1, op2) res = doMath(token, op1, op2)
tmpTokenList.append(res) tmpTokenList.append(res)
@ -118,12 +140,9 @@ def computePostfixBis(postfixExp):
tmpTokenList += tokenList tmpTokenList += tokenList
steps += expand_list(tmpTokenList) steps += expand_list(tmpTokenList)
#print("----- Steps -----")
#print(steps)
tokenList = steps[-1] tokenList = steps[-1].copy()
#print(postfixToInfix(tokenList))
return steps return steps
@ -134,7 +153,7 @@ def isNumber(exp):
:returns: True if the expression can be a number and false otherwise :returns: True if the expression can be a number and false otherwise
""" """
return type(exp) == int or type(exp) == "Fraction" return type(exp) == int or type(exp) == Fraction
def isOperation(exp): def isOperation(exp):
"""Check if the expression is an opération in "+-*/" """Check if the expression is an opération in "+-*/"
@ -155,28 +174,37 @@ def doMath(op, op1, op2):
:returns: string representing the result :returns: string representing the result
""" """
return str(eval(op1 + op + op2)) operations = {"+": "__add__", "-": "__sub__", "*": "__mul__"}
if op == "/":
ans = [Fraction(op1, op2)]
ans += ans[0].simplify()
return ans
else:
return getattr(op1,operations[op])(op2)
def postfixToInfix(postfixExp):
"""Transforme postfix expression into infix expression
:param postfixExp: a postfix expression def postfixToInfix(postfixTokens):
:returns: the corresponding infix expression """Transforms postfix list of tokens into infix string
:param postfixTokens: a postfix list of tokens
:returns: the corresponding infix string
""" """
operandeStack = Stack() operandeStack = Stack()
tokenList = postfixExp.split(" ") #print(postfixTokens)
for (i,token) in enumerate(tokenList): #tokenList = postfixExp.split(" ")
if token in "+-*/":
for (i,token) in enumerate(postfixTokens):
if isOperation(token):
op2 = operandeStack.pop() op2 = operandeStack.pop()
if needPar(op2, token, "after"): if needPar(op2, token, "after"):
op2 = "( " + op2 + " )" op2 = "( " + str(op2) + " )"
op1 = operandeStack.pop() op1 = operandeStack.pop()
if needPar(op1, token, "before"): if needPar(op1, token, "before"):
op1 = "( " + op1 + " )" op1 = "( " + str(op1) + " )"
res = "{op1} {op} {op2}".format(op1 = op1, op = token, op2 = op2) res = "{op1} {op} {op2}".format(op1 = op1, op = token, op2 = op2)
operandeStack.push(res) operandeStack.push(res)
@ -194,16 +222,17 @@ def needPar(operande, operator, posi = "after"):
:param posi: "after"(default) if the operande will be after the operator, "before" othewise :param posi: "after"(default) if the operande will be after the operator, "before" othewise
:returns: bollean :returns: bollean
""" """
priority = {"*" : 3, "/": 3, "+": 2, "-":2} priority = {"*" : 3, "/": 3, "+": 2, "-":2}
if type(operande)==int and operande < 0: if isNumber(operande) and operande < 0:
return 1 return 1
elif not isNumber(operande): elif not isNumber(operande):
# Si c'est une grande expression ou un chiffre négatif # Si c'est une grande expression ou un chiffre négatif
stand_alone = get_main_op(operande) stand_alone = get_main_op(operande)
# Si la priorité de l'operande est plus faible que celle de l'opérateur # Si la priorité de l'operande est plus faible que celle de l'opérateur
debug_var("stand_alone",stand_alone) #debug_var("stand_alone",stand_alone)
debug_var("operande", type(operande)) #debug_var("operande", type(operande))
minor_priority = priority[get_main_op(operande)] < priority[operator] minor_priority = priority[get_main_op(operande)] < priority[operator]
# Si l'opérateur est -/ pour after ou juste / pour before # Si l'opérateur est -/ pour after ou juste / pour before
special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before") special = (operator in "-/" and posi == "after") or (operator in "/" and posi == "before")
@ -212,10 +241,10 @@ def needPar(operande, operator, posi = "after"):
else: else:
return 0 return 0
def get_main_op(exp): def get_main_op(tokens):
"""Getting the main operation of the expression """Getting the main operation of the list of tokens
:param exp: the expression :param exp: the list of tokens
:returns: the main operation (+, -, * or /) or 0 if the expression is only one element :returns: the main operation (+, -, * or /) or 0 if the expression is only one element
""" """
@ -223,20 +252,20 @@ def get_main_op(exp):
priority = {"*" : 3, "/": 3, "+": 2, "-":2} priority = {"*" : 3, "/": 3, "+": 2, "-":2}
parStack = Stack() parStack = Stack()
tokenList = exp.split(" ") #tokenList = exp.split(" ")
if len(tokenList) == 1: if len(tokens) == 1:
# Si l'expression n'est qu'un élément # Si l'expression n'est qu'un élément
return 0 return 0
main_op = [] main_op = []
for token in tokenList: for token in tokens:
if token == "(": if token == "(":
parStack.push(token) parStack.push(token)
elif token == ")": elif token == ")":
parStack.pop() parStack.pop()
elif token in "+-*/" and parStack.isEmpty(): elif isOperation(token) and parStack.isEmpty():
main_op.append(token) main_op.append(token)
return min(main_op, key = lambda s: priority[s]) return min(main_op, key = lambda s: priority[s])
@ -279,9 +308,20 @@ def print_steps(steps):
:returns: @todo :returns: @todo
""" """
print("{first} \t = {sec}".format(first = steps[-1], sec = steps[-2])) print("{first} \t = {sec}".format(first = str_from_postfix(steps[0]), sec = str_from_postfix(steps[1])))
for i in steps[-2:0:-1]: for i in steps[2:]:
print("\t\t = ",i) print("\t\t = {i}".format(i=str_from_postfix(i)))
def str_from_postfix(postfix):
"""Return the string representing the expression
:param postfix: a postfix ordered list of tokens
:returns: the corresponding string expression
"""
infix = postfixToInfix(postfix)
return infix
def debug_var(name, var): def debug_var(name, var):
@ -299,11 +339,14 @@ def test(exp):
""" """
print("-------------") print("-------------")
print("Expression ",exp) print("Expression ",exp)
postfix = infixToPostfix(exp) tokens = str2tokens(exp)
postfix = infixToPostfix(tokens)
#print("Postfix " , postfix) #print("Postfix " , postfix)
#print(computePostfix(postfix)) #print(computePostfix(postfix))
#print("Bis") #print("Bis")
steps = computePostfixBis(postfix) steps = [postfix]
steps += computePostfixBis(postfix)
print(steps)
print_steps(steps) print_steps(steps)
#print(postfixToInfix(postfix)) #print(postfixToInfix(postfix))
#print(get_main_op(exp)) #print(get_main_op(exp))
@ -340,8 +383,8 @@ if __name__ == '__main__':
#exp = "( 2 + 5 ) * ( 3 * 4 )" #exp = "( 2 + 5 ) * ( 3 * 4 )"
#test(exp) #test(exp)
exp = "( 2 + 5 - 1 ) / ( 3 * 4 )" #exp = "( 2 + 5 - 1 ) / ( 3 * 4 )"
test(exp) #test(exp)
exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12" exp = "( 2 + 5 ) / ( 3 * 4 ) + 1 / 12"
test(exp) test(exp)
@ -357,8 +400,8 @@ if __name__ == '__main__':
## Ce denier pose un soucis. Pour le faire marcher il faudrai implémenter le calcul avec les fractions ## Ce denier pose un soucis. Pour le faire marcher il faudrai implémenter le calcul avec les fractions
#exp = "( 2 + 5 ) / 3 * 4" #exp = "( 2 + 5 ) / 3 * 4"
#test(exp) #test(exp)
#import doctest import doctest
#doctest.testmod() doctest.testmod()
# ----------------------------- # -----------------------------

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
from calculus import isNumber
from arithmetic import gcd from arithmetic import gcd
class Fraction(object): class Fraction(object):
@ -22,25 +21,25 @@ class Fraction(object):
:returns: steps to simplify the fraction or the fraction if there is nothing to do :returns: steps to simplify the fraction or the fraction if there is nothing to do
""" """
steps = [self] steps = []
if self._denom < 0: if self._denom < 0:
self._num = - self._num self._num = - self._num
self._denom = - self._denom self._denom = - self._denom
steps += [self] steps.append(self)
gcd_ = gcd(abs(self._num), abs(self._denom)) gcd_ = gcd(abs(self._num), abs(self._denom))
if self._num == self._denom: if self._num == self._denom:
self._num = 1 self._num = 1
self._denom = 1 self._denom = 1
steps += [self] steps.append(self)
elif gcd_ != 1: elif gcd_ != 1:
self._num, self._denom = self._num // gcd_ , self._denom // gcd_ self._num, self._denom = self._num // gcd_ , self._denom // gcd_
steps += ["( {reste1} * {gcd} ) / ( {reste2} * {gcd} )".format(reste1 = self._num, reste2 = self._denom, gcd = gcd_)] steps.append("( {reste1} * {gcd} ) / ( {reste2} * {gcd} )".format(reste1 = self._num, reste2 = self._denom, gcd = gcd_))
# Certainement le truc le plus moche que j'ai jamais fait... On ne met que des strings dans steps puis au dernier moment on met une fraction. C'est moche de ma part # Certainement le truc le plus moche que j'ai jamais fait... On ne met que des strings dans steps puis au dernier moment on met une fraction. C'est moche de ma part
steps += [self] steps.append(self)
return steps return steps
@ -50,8 +49,8 @@ class Fraction(object):
else: else:
return str(self._num) + " / " + str(self._denom) return str(self._num) + " / " + str(self._denom)
#def __repr__(self): def __repr__(self):
# return self.__str__() return "< Fraction " + self.__str__() + ">"
def __add__(self, other): def __add__(self, other):
if type(other) == Fraction: if type(other) == Fraction:
@ -60,7 +59,7 @@ class Fraction(object):
else: else:
number = Fraction(other) number = Fraction(other)
steps = ["{frac1} + {frac2}".format(frac1 = self, frac2 = number)] steps = []
if self._denom == number._denom: if self._denom == number._denom:
com_denom = self._denom com_denom = self._denom
@ -72,17 +71,18 @@ class Fraction(object):
coef1 = number._denom // gcd_denom coef1 = number._denom // gcd_denom
coef2 = self._denom // gcd_denom coef2 = self._denom // gcd_denom
steps += ["( {num1} * {coef1} ) / ( {den1} * {coef1} ) + ( {num2} * {coef2} ) / ( {den2} * {coef2} )".format(num1 = self._num, den1 = self._denom, coef1 = coef1, num2 = number._num, den2 = number._denom, coef2 = coef2)] steps.append("( {num1} * {coef1} ) / ( {den1} * {coef1} ) + ( {num2} * {coef2} ) / ( {den2} * {coef2} )".format(num1 = self._num, den1 = self._denom, coef1 = coef1, num2 = number._num, den2 = number._denom, coef2 = coef2))
com_denom = self._denom * coef1 com_denom = self._denom * coef1
num1 = self._num * coef1 num1 = self._num * coef1
num2 = number._num * coef2 num2 = number._num * coef2
steps += ["( {num1} + {num2} ) / {denom}".format(num1 = num1, num2 = num2, denom = com_denom)] steps.append("( {num1} + {num2} ) / {denom}".format(num1 = num1, num2 = num2, denom = com_denom))
num = num1 + num2 num = num1 + num2
ans_frac = Fraction(num, com_denom) ans_frac = Fraction(num, com_denom)
steps.append(ans_frac)
steps += ans_frac.simplify() steps += ans_frac.simplify()
return steps return steps
@ -94,7 +94,7 @@ class Fraction(object):
else: else:
number = Fraction(other) number = Fraction(other)
steps = ["{frac1} - {frac2}".format(frac1 = self, frac2 = number)] steps = []
if self._denom == number._denom: if self._denom == number._denom:
com_denom = self._denom com_denom = self._denom
@ -106,17 +106,18 @@ class Fraction(object):
coef1 = number._denom // gcd_denom coef1 = number._denom // gcd_denom
coef2 = self._denom // gcd_denom coef2 = self._denom // gcd_denom
steps += ["( {num1} * {coef1} ) / ( {den1} * {coef1} ) - ( {num2} * {coef2} ) / ( {den2} * {coef2} )".format(num1 = self._num, den1 = self._denom, coef1 = coef1, num2 = number._num, den2 = number._denom, coef2 = coef2)] steps.append("( {num1} * {coef1} ) / ( {den1} * {coef1} ) - ( {num2} * {coef2} ) / ( {den2} * {coef2} )".format(num1 = self._num, den1 = self._denom, coef1 = coef1, num2 = number._num, den2 = number._denom, coef2 = coef2))
com_denom = self._denom * coef1 com_denom = self._denom * coef1
num1 = self._num * coef1 num1 = self._num * coef1
num2 = number._num * coef2 num2 = number._num * coef2
steps += ["( {num1} - {num2} ) / {denom}".format(num1 = num1, num2 = num2, denom = com_denom)] steps.append("( {num1} - {num2} ) / {denom}".format(num1 = num1, num2 = num2, denom = com_denom))
num = num1 - num2 num = num1 - num2
ans_frac = Fraction(num, com_denom) ans_frac = Fraction(num, com_denom)
steps.append(ans_frac)
steps += ans_frac.simplify() steps += ans_frac.simplify()
return steps return steps
@ -128,13 +129,14 @@ class Fraction(object):
else: else:
number = Fraction(other) number = Fraction(other)
steps = ["( {frac1} ) * ( {frac2} )".format(frac1 = self, frac2 = number)] steps = []
steps += ["( {num1} * {num2} ) / ( {denom1} * {denom2} )".format(num1 = self._num, num2 = number._num, denom1 = self._denom, denom2 = number._denom)] steps.append("( {num1} * {num2} ) / ( {denom1} * {denom2} )".format(num1 = self._num, num2 = number._num, denom1 = self._denom, denom2 = number._denom))
num = self._num * number._num num = self._num * number._num
denom = self._denom * number._denom denom = self._denom * number._denom
ans_frac = Fraction(num, denom) ans_frac = Fraction(num, denom)
steps.append(ans_frac)
steps += ans_frac.simplify() steps += ans_frac.simplify()
return steps return steps
@ -146,18 +148,29 @@ class Fraction(object):
else: else:
number = Fraction(other) number = Fraction(other)
steps = ["( {frac1} ) / ( {frac2} )".format(frac1 = self, frac2 = number)] steps = []
number = Fraction(number._denom, number._num) number = Fraction(number._denom, number._num)
steps += self * number steps += self * number
return steps return steps
def __lt__(self, other):
if type(other) == Fraction:
return (self._num / self._denom) < (other._num / other._denom)
else:
return (self._num / self._denom) < other
def __le__(self, other):
if type(other) == Fraction:
return (self._num / self._denom) <= (other._num / other._denom)
else:
return (self._num / self._denom) <= other
if __name__ == '__main__': if __name__ == '__main__':
f = Fraction(34, 12) f = Fraction(1, 12)
g = Fraction(1,5) g = Fraction(1, 12)
h = Fraction(-1,5) h = Fraction(-1,5)
t = Fraction(-4,5) t = Fraction(-4,5)
print("---------") print("---------")
@ -169,24 +182,24 @@ if __name__ == '__main__':
print("---------") print("---------")
for i in (f + g): for i in (f + g):
print(i) print(i)
print("---------") #print("---------")
for i in (f - g): #for i in (f - g):
print(i) # print(i)
print("---------") #print("---------")
for i in (f * g): #for i in (f * g):
print(i) # print(i)
print("---------") #print("---------")
for i in (h + t): #for i in (h + t):
print(i) # print(i)
print("---------") #print("---------")
for i in (h - t): #for i in (h - t):
print(i) # print(i)
print("---------") #print("---------")
for i in (h * t): #for i in (h * t):
print(i) # print(i)
print("---------") #print("---------")
for i in (h / t): #for i in (h / t):
print(i) # print(i)
#print(f.simplify()) #print(f.simplify())