Mapytex/mapytex/calculus/API/expression.py

276 lines
6.3 KiB
Python

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2017 lafrite <lafrite@Poivre>
#
# Distributed under terms of the MIT license.
"""
Expression
"""
from ..core import AssocialTree, Tree, compute, typing
from .renders import renders
class Expression(object):
"""
Expression class
:example:
>>> e = Expression.from_str("2+3*4")
>>> e2 = e.simplify()
>>> print(e2)
14
>>> for s in e2.explain():
... print(s)
2 + 3 * 4
2 + 12
14
"""
RENDER = 'txt'
def __init__(self, tree, ancestor=None):
"""
"""
self._tree = tree
self._ancestor = ancestor
@classmethod
def from_str(cls, string):
""" Initiate the expression from a string
:param string: String to parse to generate the Expression
:returns: the expression
"""
t = Tree.from_str(string)
return cls(t)
@classmethod
def random(self, template, conditions = [], shuffle = False):
""" Initiate randomly the expression
:param template: the template of the expression
:param conditions: conditions on randomly generate variable
:param shuffle: allowing to shuffle the tree
:returns: TODO
"""
pass
@classmethod
def set_render(cls, render):
""" Define default render function
:param render: render function Tree -> str
:example:
>>> Expression.RENDER
'txt'
>>> e = Expression.from_str("2+3*4")
>>> print(e)
2 + 3 * 4
>>> Expression.set_render('tex')
>>> Expression.RENDER
'tex'
>>> print(e)
2 + 3 \\times 4
"""
cls.RENDER = render
def __str__(self):
return renders[self.RENDER](self._tree)
def __repr__(self):
return f"<Exp: {renders[self.RENDER](self._tree)}>"
def balance(self):
""" Balance selt._tree in order to optimize end tree number
# TODO: their will be issue with / which is not commutative |lun. oct. 1 15:03:21 CEST 2018
:return: optmized version of self
:example:
>>> t = Expression.from_str("1+2+3+4+5+6+7+8+9")
>>> print(t._tree)
+
> +
| > +
| | > +
| | | > +
| | | | > +
| | | | | > +
| | | | | | > +
| | | | | | | > 1
| | | | | | | > 2
| | | | | | > 3
| | | | | > 4
| | | | > 5
| | | > 6
| | > 7
| > 8
> 9
>>> t.balance()
>>> print(t._tree)
+
> +
| > +
| | > 1
| | > 2
| > +
| | > 3
| | > 4
> +
| > +
| | > 5
| | > 6
| > +
| | > 7
| | > +
| | | > 8
| | | > 9
>>> t = Expression.from_str("1+2+3+4+5*6*7*8*9")
>>> print(t._tree)
+
> +
| > +
| | > +
| | | > 1
| | | > 2
| | > 3
| > 4
> *
| > *
| | > *
| | | > *
| | | | > 5
| | | | > 6
| | | > 7
| | > 8
| > 9
>>> t.balance()
>>> print(t._tree)
+
> +
| > 1
| > 2
> +
| > 3
| > +
| | > 4
| | > *
| | | > *
| | | | > 5
| | | | > 6
| | | > *
| | | | > 7
| | | | > *
| | | | | > 8
| | | | | > 9
"""
self._tree = AssocialTree.from_any_tree(self._tree).balance()
def simplify(self, optimize=True):
""" Compute as much as possible the expression
:param optimize: bool to optimize tree when it's possible
:return: an expression
:example:
>>> e = Expression.from_str("2+3*4")
>>> e
<Exp: 2 + 3 \\times 4>
>>> f = e.simplify()
>>> f
<Exp: 14>
>>> f._ancestor
<Exp: 2 + 12>
"""
if optimize:
try:
self.balance()
except AttributeError:
pass
try:
t = self._tree.apply_on_last_level(compute, typing)
except AttributeError:
return self
except NotImplementedError:
return self
else:
e = Expression(t, ancestor=self)
return e.simplify(optimize=optimize)
def explain(self):
""" Yield every calculus step which have lead to self
:example:
>>> e = Expression.from_str("2+3*4")
>>> f = e.simplify()
>>> for s in f.explain():
... print(s)
2 + 3 * 4
2 + 12
14
>>> e = Expression.from_str("1+2+3+4+5+6+7+8+9")
>>> f = e.simplify()
>>> for s in f.explain():
... print(s)
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
3 + 7 + 11 + 7 + 17
10 + 11 + 24
10 + 35
45
>>> e = Expression.from_str("1+2+3+4+5+6+7+8+9")
>>> f_no_balance = e.simplify(optimize=False)
>>> for s in f_no_balance.explain():
... print(s)
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
3 + 3 + 4 + 5 + 6 + 7 + 8 + 9
6 + 4 + 5 + 6 + 7 + 8 + 9
10 + 5 + 6 + 7 + 8 + 9
15 + 6 + 7 + 8 + 9
21 + 7 + 8 + 9
28 + 8 + 9
36 + 9
45
>>> e = Expression.from_str("1+2+3+4+5*6*7*8*9")
>>> f = e.simplify()
>>> for s in f.explain():
... print(s)
1 + 2 + 3 + 4 + 5 * 6 * 7 * 8 * 9
3 + 3 + 4 + 30 * 7 * 72
6 + 4 + 30 * 504
6 + 4 + 15120
6 + 15124
15130
>>> e = Expression.from_str("1+2+3+4+5*6*7*8*9")
>>> f_no_balance = e.simplify(optimize=False)
>>> for s in f_no_balance.explain():
... print(s)
1 + 2 + 3 + 4 + 5 * 6 * 7 * 8 * 9
3 + 3 + 4 + 30 * 7 * 8 * 9
6 + 4 + 210 * 8 * 9
10 + 1680 * 9
10 + 15120
15130
"""
try:
yield from self._ancestor.explain()
except AttributeError:
yield self
else:
yield self
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del