formal (which should be called polynom...) are ok.No mult yet

This commit is contained in:
lafrite 2014-01-17 20:17:49 +01:00
parent 999fde1c51
commit 039f27c4fc
4 changed files with 259 additions and 36 deletions

View File

@ -4,6 +4,8 @@
from generic import Stack, flatten_list, expand_list from generic import Stack, flatten_list, expand_list
from fraction import Fraction from fraction import Fraction
from render import txt_render, post2in_fix, tex_render from render import txt_render, post2in_fix, tex_render
from formal import FormalExp
from formal import FormalExp
class Expression(object): class Expression(object):
"""A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown"""
@ -32,7 +34,7 @@ class Expression(object):
def render(self, render = lambda x:str(x)): def render(self, render = lambda x:str(x)):
""" Same as __str__ but accept render as argument """ Same as __str__ but accept render as argument
@param render: function which render the list of token (postfix form) to string :param render: function which render the list of token (postfix form) to string
""" """
# TODO: I don't like the name of this method |ven. janv. 17 12:48:14 CET 2014 # TODO: I don't like the name of this method |ven. janv. 17 12:48:14 CET 2014
@ -44,7 +46,7 @@ class Expression(object):
def simplify(self, render = lambda x:str(x)): def simplify(self, render = lambda x:str(x)):
""" Generator which return steps for computing the expression """ Generator which return steps for computing the expression
@param render: function which render the list of token (postfix form now) to string :param render: function which render the list of token (postfix form now) to string
""" """
if not self.can_go_further(): if not self.can_go_further():
@ -123,8 +125,10 @@ class Expression(object):
for character in exp: for character in exp:
if character.isdigit(): if character.isdigit():
# for "big" numbers (like 2345)
if type(tokens[-1]) == int: if type(tokens[-1]) == int:
tokens[-1] = tokens[-1]*10 + int(character) tokens[-1] = tokens[-1]*10 + int(character)
# Special case for "-" at the begining of an expression or before "(" # Special case for "-" at the begining of an expression or before "("
elif tokens[-1] == "-" and \ elif tokens[-1] == "-" and \
str(tokens[-2]) in " (": str(tokens[-2]) in " (":
@ -133,16 +137,29 @@ class Expression(object):
tokens.append(int(character)) tokens.append(int(character))
elif character.isalpha(): elif character.isalpha():
if str(tokens[-1]).isalpha(): # If "3x", ")x" or "yx"
tokens[-1] += character if self.isNumber(tokens[-1]) \
or tokens[-1] == ")" \
or type(tokens[-1]) == FormalExp:
tokens.append("*")
tokens.append(FormalExp(letter = character))
# Special case for "-" at the begining of an expression or before "("
elif tokens[-1] == "-" \
or str(tokens[-2]) in " (":
tokens[-1] = - FormalExp(letter = character)
else: else:
tokens.append(character) tokens.append(FormalExp(letter = character))
elif character in "+-*/)": elif character in "+-*/)":
tokens.append(character) tokens.append(character)
elif character in "(": elif character in "(":
if self.isNumber(tokens[-1]) or tokens[-1] == ")": # If "3(", ")(" or "x("
if self.isNumber(tokens[-1]) \
or tokens[-1] == ")" \
or type(tokens[-1]) == FormalExp:
tokens.append("*") tokens.append("*")
tokens.append(character) tokens.append(character)
@ -278,14 +295,19 @@ class Expression(object):
:returns: string representing the result :returns: string representing the result
""" """
operations = {"+": "__add__", "-": "__sub__", "*": "__mul__"}
if op == "/": if op == "/":
ans = [Fraction(op1, op2)] ans = [Fraction(op1, op2)]
ans += ans[0].simplify() ans += ans[0].simplify()
return ans return ans
else: else:
if type(op2) != int:
operations = {"+": "__radd__", "-": "__rsub__", "*": "__rmul__"}
return getattr(op2,operations[op])(op1)
else:
operations = {"+": "__add__", "-": "__sub__", "*": "__mul__"}
return getattr(op1,operations[op])(op2) return getattr(op1,operations[op])(op2)
## --------------------- ## ---------------------
## Recognize numbers and operators ## Recognize numbers and operators
@ -299,7 +321,7 @@ class Expression(object):
""" """
return type(exp) == int or \ return type(exp) == int or \
type(exp) == Fraction or \ type(exp) == Fraction or \
exp.isalpha() type(exp) == FormalExp
@staticmethod @staticmethod
def isOperator(exp): def isOperator(exp):
@ -322,17 +344,17 @@ def test(exp):
print("\n") print("\n")
if __name__ == '__main__': if __name__ == '__main__':
exp = "1 + 3 * 5" #exp = "1 + 3 * 5"
test(exp) #test(exp)
#exp = "2 * 3 * 3 * 5" #exp = "2 * 3 * 3 * 5"
#test(exp) #test(exp)
exp = "2 * 3 + 3 * 5" #exp = "2 * 3 + 3 * 5"
test(exp) #test(exp)
exp = "2 * ( 3 + 4 ) + 3 * 5" #exp = "2 * ( 3 + 4 ) + 3 * 5"
test(exp) #test(exp)
#exp = "2 * ( 3 + 4 ) + ( 3 - 4 ) * 5" #exp = "2 * ( 3 + 4 ) + ( 3 - 4 ) * 5"
#test(exp) #test(exp)
@ -352,25 +374,29 @@ 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)
exp = "( 2+ 5 )/( 3 * 4 ) + 1 / 2" #exp = "( 2+ 5 )/( 3 * 4 ) + 1 / 2"
test(exp) #test(exp)
exp="(-2+5)/(3*4)+1/12+5*5" #exp="(-2+5)/(3*4)+1/12+5*5"
test(exp) #test(exp)
exp="-2*4(12 + 1)(3-12)" exp="-2*4(12 + 1)(3-12)"
test(exp) test(exp)
exp="-2*a(12 + 1)(3-12)" exp="-2+a+(12 + 1)(3-12) + 34a"
test(exp)
e = Expression(exp) e = Expression(exp)
print(e) print(e)
#exp="-2*b+a(12 + 1)(3-12)"
#test(exp)
# TODO: The next one doesn't work |ven. janv. 17 14:56:58 CET 2014 # TODO: The next one doesn't work |ven. janv. 17 14:56:58 CET 2014
#exp="-2*(-a)(12 + 1)(3-12)" #exp="-2*(-a)(12 + 1)(3-12)"
#e = Expression(exp) #e = Expression(exp)

128
formal.py Normal file
View File

@ -0,0 +1,128 @@
#!/usr/bin/env python
# encoding: utf-8
from fraction import Fraction
from generic import add_in_dict, remove_in_dict
import re
class FormalExp(object):
"""A formal expression (similare to Symbol in Sympy"""
def __init__(self, coef = {}, letter = ""):
"""Initiat the formal expression
:param coef: the dictionary representing the expression
:param letter: minimum expression, a letter
"""
if coef != {} and letter != "":
raise ValueError("A FormalExp can't be initiate with dict_exp and a letter")
elif letter != "":
self._letter = letter
self._coef = {letter: 1}
elif coef != {}:
self._coef = coef
else:
raise ValueError("FormalExp needs a letter or dictionary of coeficients")
if len(self) != 1:
self.mainOp = "+"
def master_coef(self):
"""Return the master coefficient
/!\ may not work pretty well if there is more than one indeterminate
:returns: a_n
"""
pattern = "\w\*\*(\d*)"
finder = re.compile(pattern)
power = {}
for (k,v) in self._coef.items():
if k=="":
power[0] = v
else:
p = finder.findall(k)
if p == []:
power[1] = v
else:
power[int(p[0])] = v
m_power = max(power)
return power[m_power]
def __add__(self, other):
if type(other) in [int, Fraction]:
d = {"":other}
elif type(other) == FormalExp:
d = other._coef
else:
raise ValueError("Can't add {type} with FormalExp".format(type=type(other)))
d = add_in_dict(self._coef, d)
d = remove_in_dict(d)
if list(d.keys()) == ['']:
return [d['']]
else:
return [FormalExp(d)]
def __radd__(self, other):
return self + other
def __sub__(self, other):
o_tmp = -other
return self + o_tmp
def __neg__(self):
d = {}
for k,v in self._coef.items():
d[k] = -v
return FormalExp(d)
def __mul__(self, other):
pass
def __rmul__(self, other):
pass
def __div__(self, other):
pass
def __pow__(self, other):
pass
def __len__(self):
return len(list(self._coef.keys()))
def __str__(self):
return " + ".join([str(v) + str(k) for k,v in self._coef.items()])
if __name__ == '__main__':
#fe1 = FormalExp({"x": 1, "":2})
#print(fe1)
#fe2 = FormalExp({"x**12": 5, "":2})
#print(fe2)
#fe3 = fe1 + fe2
#for s in fe3:
# print(s)
#fe4 = fe1 + 2
#for s in fe4:
# print(s)
#print(fe1.master_coef())
#print(fe2.master_coef())
#print(fe3[0].master_coef())
#print(fe4[0].master_coef())
fe = FormalExp(letter = "a")
fe_ = -2 + fe
print(fe_[0])
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -99,6 +99,54 @@ def expand_list(list_list):
ans = [list_list] ans = [list_list]
return ans return ans
def add_in_dict(dict1, dict2):
"""Merge dictionary keys and add the content from dict1 and dict2
:param dict1: first dictionary
:param dict2: second dictionary
:returns: merged and added dictionary
>>> add_in_dict({'a':1, 'b':2}, {'c':3, 'd': 4}) == {'d': 4, 'a': 1, 'c': 3, 'b': 2}
True
>>> add_in_dict({'a':1, 'b':2}, {'a':3, 'b': 4}) == {'a': 4, 'b': 6}
True
>>> add_in_dict({'a':1, 'b':2}, {'a':3, 'c': 4}) == {'a': 4, 'b': 2, 'c': 4}
True
"""
new_dict = {}
new_dict.update(dict1)
for (k,v) in dict2.items():
if k in new_dict.keys():
new_dict[k] += v
else:
new_dict[k] = v
return new_dict
def remove_in_dict(d, value = 0):
""" In a dictionary, remove keys which have certain value
:param d: the dictionary
:param value: value to remove
:returns: new dictionary whithout unwanted value
>>> remove_in_dict({'b': 1, 'a': 0}) == {'b': 1}
True
>>> remove_in_dict({'b': 1, 'a': 0}, 1) == {'a': 0}
True
"""
new_dict = {}
for (k,v) in d.items():
if v != value:
new_dict[k] = v
return new_dict
if __name__ == '__main__':
import doctest
doctest.testmod()
# ----------------------------- # -----------------------------
# Reglages pour 'vim' # Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4: # vim:set autoindent expandtab tabstop=4 shiftwidth=4:

View File

@ -3,6 +3,7 @@
from generic import Stack,flatten_list from generic import Stack,flatten_list
from fraction import Fraction from fraction import Fraction
from formal import FormalExp
@ -16,7 +17,7 @@ class Render(object):
PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1} PRIORITY = {"*" : 3, "/": 3, "+": 2, "-":2, "(": 1}
def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, str: str}): def __init__(self, op_infix = {}, op_postfix = {}, other = {}, join = " ", type_render = {int: str, Fraction: str, FormalExp: str}):
"""Initiate the render """Initiate the render
@param op_infix: the dictionnary of infix operator with how they have to be render @param op_infix: the dictionnary of infix operator with how they have to be render
@ -71,11 +72,12 @@ class Render(object):
else: else:
operandeStack.push(token) operandeStack.push(token)
# Manip pour gerer les cas de listes imbriquées dans d'autres listes # Manip pour gerer les cas de listes imbriquées dans d'autres listes
infix_tokens = operandeStack.pop() infix_tokens = operandeStack.pop()
if type(infix_tokens) == list or type(infix_tokens) == flist: if type(infix_tokens) == list or type(infix_tokens) == flist:
infix_tokens = flatten_list(infix_tokens) infix_tokens = flatten_list(infix_tokens)
elif self.isNumber(infix_tokens): elif self.isNumerande(infix_tokens):
infix_tokens = [infix_tokens] infix_tokens = [infix_tokens]
if self.join: if self.join:
@ -84,13 +86,13 @@ class Render(object):
return infix_tokens return infix_tokens
def render_from_type(self, op): def render_from_type(self, op):
""" If the op is a number, it transforms it with type_render conditions """ If the op is a numerande, it transforms it with type_render conditions
:param op: the operator :param op: the operator
:returns: the op transformed if it's necessary :returns: the op transformed if it's necessary
""" """
if self.isNumber(op): if self.isNumerande(op):
return self.type_render[type(op)](op) return self.type_render[type(op)](op)
else: else:
return op return op
@ -108,9 +110,17 @@ class Render(object):
:returns: bollean :returns: bollean
""" """
if self.isNumber(operande) \ if self.isNumber(operande) \
and type(operande) != str \
and operande < 0: and operande < 0:
return 1 return 1
elif type(operande) == FormalExp:
if operator in ["*", "/"]:
if len(operande) > 1 \
or operande.master_coef() < 0:
return 1
else:
return 0
elif not self.isNumber(operande): elif not self.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 = self.get_main_op(operande) stand_alone = self.get_main_op(operande)
@ -155,17 +165,28 @@ class Render(object):
@staticmethod @staticmethod
def isNumber( exp): def isNumber( exp):
"""Check if the expression can be a number which means that it is not a operator """Check if the expression can be a number which means int or Fraction
:param exp: an expression :param exp: an expression
: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 \ return type(exp) == int \
type(exp) == Fraction or \ or type(exp) == Fraction
(type(exp) == str and exp.isalpha())
#return type(exp) == int or type(exp) == Fraction #return type(exp) == int or type(exp) == Fraction
@staticmethod
def isNumerande(exp):
"""Check if the expression can be a numerande (not an operator)
:param exp: an expression
:returns: True if the expression can be a number and false otherwise
"""
return type(exp) == int \
or type(exp) == Fraction \
or type(exp) == FormalExp
def isOperator(self, exp): def isOperator(self, exp):
"""Check if the expression is in self.operators """Check if the expression is in self.operators
@ -202,9 +223,9 @@ post2in_fix = Render(p2i_infix, p2i_postfix, p2i_other, join = False)
# A latex render # A latex render
def texSlash(op1, op2): def texSlash(op1, op2):
if not Render.isNumber(op1) and op1[0] == "(" and op1[-1] == ")": if not Render.isNumerande(op1) and op1[0] == "(" and op1[-1] == ")":
op1 = op1[1:-1] op1 = op1[1:-1]
if not Render.isNumber(op2) and op2[0] == "(" and op2[-1] == ")": if not Render.isNumerande(op2) and op2[0] == "(" and op2[-1] == ")":
op2 = op2[1:-1] op2 = op2[1:-1]
return ["\\frac{" , op1 , "}{" , op2 , "}"] return ["\\frac{" , op1 , "}{" , op2 , "}"]
@ -214,7 +235,7 @@ def texFrac(frac):
tex_infix = {"+": " + ", "-": " - ", "*": " \\times "} tex_infix = {"+": " + ", "-": " - ", "*": " \\times "}
tex_postfix = {"/": texSlash} tex_postfix = {"/": texSlash}
tex_other = {"(": "(", ")": ")"} tex_other = {"(": "(", ")": ")"}
tex_type_render = {int: str, Fraction: texFrac, str: str} tex_type_render = {int: str, Fraction: texFrac, FormalExp: str}
tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render) tex_render = Render(tex_infix, tex_postfix, tex_other, type_render = tex_type_render)