#!/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 >>> 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 >>> 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