#! /usr/bin/env python # -*- coding: utf-8 -*- # vim:fenc=utf-8 # # Copyright © 2017 lafrite # # Distributed under terms of the MIT license. """ Expression """ from ..core import AssocialTree, Tree, compute, typing, TypingError 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"" def _optimize(self, exclude_nodes=["\\", "**"]): """ Return a copy of self with an optimize tree """ try: # TODO: need to test exclude_nodes |ven. oct. 5 08:51:02 CEST 2018 return Expression(self._tree.balance(exclude_nodes=exclude_nodes)) except AttributeError: return self def _typing(self): """ Build a copy of self with as much typing as possible :example: >>> e = Expression.from_str("2x") >>> print(e._tree.map_on_leaf(type)) * > > >>> typed_e = e._typing() >>> print(type(typed_e._tree)) >>> typed_e = e._typing() >>> print(type(typed_e._tree)) >>> e = Expression.from_str("2x+3+4/5") >>> print(e._tree.map_on_leaf(type)) + > + | > * | | > | | > | > > / | > | > >>> typed_e = e._typing() >>> print(typed_e._tree.map_on_leaf(type)) + > > """ try: t = self._tree.apply_on_last_level(typing) except TypingError: return self except NotImplementedError: return self except AttributeError: return self else: return Expression(t)._typing() def _compute(self): """" Compute one step of self """ try: return Expression(self._tree.apply_on_last_level(compute)) except AttributeError: return self def set_ancestor(self, ancestor): """ Set ancestor """ self._ancestor = ancestor 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 >>> f = e.simplify() >>> f >>> f._ancestor """ if optimize: opt_exp = self._optimize() else: opt_exp = self typed_exp = opt_exp._typing() comp_exp = typed_exp._compute() if typed_exp == comp_exp: return self else: comp_exp.set_ancestor(self) return comp_exp.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 + 3 + 9 + 6 + 7 + 17 6 + 9 + 6 + 24 15 + 30 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 + 210 * 72 10 + 15120 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 >>> e = Expression.from_str("1+2/3/4/5") >>> f = e.simplify() >>> for s in f.explain(): ... print(s) 1 + 2 / 3 / 4 / 5 1 + (2 / 3 * 1 / 4) / 5 1 + (2 * 1) / (3 * 4) / 5 1 + 2 / 12 / 5 1 + 2 / 12 * 1 / 5 1 + (2 * 1) / (12 * 5) 1 + 2 / 60 1 / 1 + 2 / 60 (1 * 60) / (1 * 60) + 2 / 60 60 / 60 + 2 / 60 (60 + 2) / 60 62 / 60 """ 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