Mapytex/pymath/calculus/explicable.py

273 lines
8.0 KiB
Python

#!/usr/bin/env python
# encoding: utf-8
from .renderable import Renderable
from .step import Step
from decimal import Decimal
from .generic import transpose_fill
class Explicable(Renderable):
""" An Explicable object is an object which can be explicable!
It's a parent class of a more classical Expression, Fraction and Polynom (and later square root)
Child class will have the following method
* explain: Generator which return steps which leed to himself
"""
def __init__(self, pstf_tokens):
super(Explicable, self).__init__(pstf_tokens)
self.steps = []
self.explained = 0
def explain(self):
r""" Generate and render steps which leed to itself
After beening explained, the Explicable become amnesiac. It will return itself in a list.
See 'history_generator' to explain it once again.
>>> action = Explicable(['42'])
>>> from .expression import Expression
>>> action.this_append_before([Expression('2+10*4'), Expression('2+40')])
>>> for i in action.explain():
... print(i)
2 + 10 \times 4
2 + 40
42
>>> # Now action is amnesiac
>>> for i in action.explain():
... print(i)
42
>>> action = Explicable(['42'])
>>> for i in action.explain():
... print(i)
42
"""
if self.explained:
return [self.STR_RENDER(self.postfix_tokens)]
else:
self.explained = 1
return self.history_generator()
def history_generator(self):
r""" Generator for rendered steps which leed to itself
This is the called method in explain which create the generator.
It create a new generator at each call.
>>> action = Explicable(['42'])
>>> from .expression import Expression
>>> for i in action.history_generator():
... print(i)
42
>>> action.this_append_before([Expression('2+10*4'), Expression('2+40')])
>>> for i in action.history_generator():
... print(i)
2 + 10 \times 4
2 + 40
42
>>> for i in action.history_generator():
... print(i)
2 + 10 \times 4
2 + 40
42
"""
old_s = ''
try:
for s in self.steps:
try:
new_s = self.STR_RENDER(s.postfix_tokens)
except AttributeError:
new_s = self.STR_RENDER(s)
if not self.is_same_step(new_s, old_s):
old_s = new_s
yield new_s
except AttributeError:
pass
# if noself:
new_s = self.STR_RENDER(self.postfix_tokens)
if not self.is_same_step(new_s, old_s):
yield new_s
def is_same_step(self, new, old):
"""Return whether the new step is the same than old step
"""
try:
if new.replace(" ", "") == old.replace(" ", ""):
return True
else:
return False
except AttributeError:
if new == old:
return True
else:
return False
def this_append_before(self, steps):
""" Add steps at the beginning of self.steps
:param steps: list of steps that append before
>>> e = Explicable(['Actions'])
>>> s = ['eat', 'sleep']
>>> e.this_append_before(s)
>>> print(e.steps)
['eat', 'sleep']
>>> s0 = ['cook']
>>> e.this_append_before(s0)
>>> print(e.steps)
['cook', 'eat', 'sleep']
"""
self.steps = list(steps) + self.steps
def steal_history(self, arg1):
""" Steal the history of arg1 for itself
:param arg1: a potential Explicable
>>> e = Explicable(['Actions'])
>>> s = [Step(['eat']), Step(['sleep'])]
>>> e.this_append_before(s)
>>> f = Explicable(['Stolen actions'])
>>> f.steal_history(e)
>>> for i in e.explain():
... print(i)
Actions
>>> for i in f.explain():
... print(i)
eat
sleep
Actions
Stolen actions
"""
try:
with Step.tmp_render():
self.this_append_before(list(arg1.explain()))
except AttributeError:
pass
@classmethod
def merge_history(cls, explicables):
r""" Take a list of Explicable objects, explain where they are from and merge their history
This method try to use 'explain' method from Explicable. This means that after using this method, those objects will become amnesiac.
:param explicables: list
:returns: the list of steps
>>> from .expression import Expression
>>> action1 = Explicable(['42'])
>>> action1.this_append_before([['2 + 4 * 10'], ['2 + 40']])
>>> action2 = Explicable(['24'])
>>> action2.this_append_before([['12 * 2']])
>>> m_history = Explicable.merge_history([action1, action2])
>>> m_history
<generator object transpose_fill ...>
>>> for i in m_history:
... print(i)
[< Step ['2 + 4 * 10']>, < Step ['12 * 2']>]
[< Step ['2 + 40']>, < Step ['24']>]
[< Step ['42']>, < Step ['24']>]
>>> # Now they are amnesiac
>>> for i in action1.explain():
... print(i)
42
>>> m_history = Explicable.merge_history([action1, action2])
>>> for i in m_history:
... print(i)
[< Step ['42']>, < Step ['24']>]
>>> action1 = Explicable(['42'])
>>> action1.this_append_before([['2+10*4'], ['2+40']])
>>> m_history = Explicable.merge_history([action1, 12])
>>> m_history
<generator object transpose_fill ...>
>>> for i in m_history:
... print(i)
[< Step ['2+10*4']>, < Step [12]>]
[< Step ['2+40']>, < Step [12]>]
[< Step ['42']>, < Step [12]>]
>>> action1 = Explicable(['42'])
>>> action1.this_append_before([['2+10*4'], ['2+40']])
>>> action2 = Explicable(['24'])
>>> action2.this_append_before([['2*12']])
>>> action3 = Explicable(['0'])
>>> action3.this_append_before([['3-3']])
>>> m_history = Explicable.merge_history([action1, action2, action3])
>>> for i in m_history:
... print(i)
[< Step ['2+10*4']>, < Step ['2*12']>, < Step ['3-3']>]
[< Step ['2+40']>, < Step ['24']>, < Step ['0']>]
[< Step ['42']>, < Step ['24']>, < Step ['0']>]
>>> m_history = Explicable.merge_history([1,2,3])
>>> for i in m_history:
... print(i)
[< Step [1]>, < Step [2]>, < Step [3]>]
"""
steps = []
for expl in explicables:
try:
with Step.tmp_render():
steps.append(list(expl.explain()))
except AttributeError:
steps.append([Step([expl])])
return transpose_fill(steps)
class Explicable_int(int, Explicable):
""" An Explicable_int is an int which can be explain """
isNumber = True
def __init__(self, val):
super(Explicable_int, self).__init__(val)
self._val = val
self.postfix_tokens = [self]
self.steps = []
def simplify(self):
return Explicable_int(self._val)
def __txt__(self):
return str(self._val)
def __tex__(self):
return str(self._val)
class Explicable_Decimal(Decimal, Explicable):
""" An Explicable_Decimal is an decimal which can be explain """
isNumber = True
def __init__(self, val):
super(Explicable_Decimal, self).__init__(val)
self._val = val
self.postfix_tokens = [self]
self.steps = []
def simplify(self):
return Explicable_Decimal(self._val)
def __txt__(self):
return str(self._val)
def __tex__(self):
return str(self._val)
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del