2018-01-21 09:21:13 +00:00
|
|
|
#! /usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# vim:fenc=utf-8
|
|
|
|
#
|
|
|
|
# Copyright © 2017 lafrite <lafrite@Poivre>
|
|
|
|
#
|
|
|
|
# Distributed under terms of the MIT license.
|
|
|
|
|
|
|
|
"""
|
|
|
|
Expression
|
|
|
|
|
|
|
|
"""
|
2019-07-15 16:37:16 +00:00
|
|
|
from functools import partial
|
2018-11-19 11:23:37 +00:00
|
|
|
from ..core import AssocialTree, Tree, compute, typing, TypingError
|
2019-10-13 19:01:31 +00:00
|
|
|
from ..core.random import (
|
|
|
|
extract_rdleaf,
|
|
|
|
extract_rv,
|
|
|
|
random_generator,
|
|
|
|
compute_leafs,
|
|
|
|
replace_rdleaf,
|
|
|
|
)
|
2019-05-16 15:21:34 +00:00
|
|
|
from ..core.MO import moify
|
2019-07-15 09:09:59 +00:00
|
|
|
from .tokens import factory
|
2020-12-15 14:37:27 +00:00
|
|
|
from .renders import render
|
2018-01-21 09:21:13 +00:00
|
|
|
|
2019-05-14 04:55:56 +00:00
|
|
|
|
2018-01-21 09:21:13 +00:00
|
|
|
class Expression(object):
|
|
|
|
|
|
|
|
"""
|
|
|
|
Expression class
|
|
|
|
|
2018-09-17 14:29:00 +00:00
|
|
|
:example:
|
|
|
|
|
|
|
|
>>> e = Expression.from_str("2+3*4")
|
|
|
|
>>> e2 = e.simplify()
|
|
|
|
>>> print(e2)
|
|
|
|
14
|
2018-09-17 16:18:29 +00:00
|
|
|
>>> for s in e2.explain():
|
|
|
|
... print(s)
|
|
|
|
2 + 3 * 4
|
|
|
|
2 + 12
|
|
|
|
14
|
2018-01-21 09:21:13 +00:00
|
|
|
"""
|
|
|
|
|
2018-09-17 14:29:00 +00:00
|
|
|
|
2018-09-17 16:18:29 +00:00
|
|
|
def __init__(self, tree, ancestor=None):
|
2018-01-21 09:21:13 +00:00
|
|
|
"""
|
|
|
|
"""
|
2018-09-17 14:29:00 +00:00
|
|
|
self._tree = tree
|
2018-09-17 16:18:29 +00:00
|
|
|
self._ancestor = ancestor
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
@classmethod
|
2018-12-13 08:29:44 +00:00
|
|
|
def from_str(cls, string, typing=True):
|
2018-01-21 09:21:13 +00:00
|
|
|
""" Initiate the expression from a string
|
|
|
|
|
2018-09-20 16:27:40 +00:00
|
|
|
:param string: String to parse to generate the Expression
|
|
|
|
:returns: the expression
|
2018-01-21 09:21:13 +00:00
|
|
|
|
2018-12-13 08:29:44 +00:00
|
|
|
:example:
|
|
|
|
>>> e = Expression.from_str("2 + 3 * 4")
|
|
|
|
>>> e
|
|
|
|
<Exp: 2 + 3 * 4>
|
|
|
|
>>> e = Expression.from_str("2/3")
|
|
|
|
>>> e
|
|
|
|
<Fraction 2 / 3>
|
|
|
|
>>> e = Expression.from_str("2x + 1")
|
|
|
|
>>> e
|
|
|
|
<Linear 2x + 1>
|
|
|
|
>>> e = Expression.from_str("2x + 1 + 5x^2")
|
|
|
|
>>> e
|
2019-10-30 20:12:58 +00:00
|
|
|
<Quadratic 5x^2 + 2x + 1>
|
2018-12-13 08:29:44 +00:00
|
|
|
>>> e = Expression.from_str("2x + 1 + 5x")
|
|
|
|
>>> e
|
|
|
|
<Exp: 2x + 1 + 5x>
|
2018-01-21 09:21:13 +00:00
|
|
|
"""
|
2018-09-17 14:29:00 +00:00
|
|
|
t = Tree.from_str(string)
|
2018-12-13 08:29:44 +00:00
|
|
|
if typing:
|
2019-10-13 19:11:05 +00:00
|
|
|
return cls._post_processing(t)
|
2018-12-13 08:29:44 +00:00
|
|
|
|
2018-09-17 14:29:00 +00:00
|
|
|
return cls(t)
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
@classmethod
|
2019-05-16 15:21:34 +00:00
|
|
|
def random(
|
|
|
|
cls,
|
|
|
|
template,
|
|
|
|
conditions=[],
|
|
|
|
rejected=[0],
|
|
|
|
min_max=(-10, 10),
|
|
|
|
variables_scope={},
|
|
|
|
shuffle=False,
|
|
|
|
):
|
|
|
|
""" Initiate randomly the expression
|
2018-01-21 09:21:13 +00:00
|
|
|
|
|
|
|
:param template: the template of the expression
|
|
|
|
:param conditions: conditions on randomly generate variable
|
2019-05-16 15:21:34 +00:00
|
|
|
:param rejected: Values to reject for all random variables
|
|
|
|
:param min_max: Min and max value for all random variables
|
|
|
|
:param variables_scope: Dictionnary for each random varaibles to fic rejected and min_max
|
2018-01-21 09:21:13 +00:00
|
|
|
:param shuffle: allowing to shuffle the tree
|
|
|
|
:returns: TODO
|
|
|
|
|
2019-05-16 15:21:34 +00:00
|
|
|
:example:
|
2020-08-21 17:07:34 +00:00
|
|
|
>>> e = Expression.random("{a}/{a*k}")
|
|
|
|
>>> e # doctest: +SKIP
|
2019-05-16 15:21:34 +00:00
|
|
|
<Exp: -3 / -15>
|
2020-08-21 17:07:34 +00:00
|
|
|
>>> e = Expression.random("{a}/{a*k} - 3*{b}", variables_scope={'a':{'min_max':(10, 30)}})
|
|
|
|
>>> e # doctest: +SKIP
|
2019-05-16 15:21:34 +00:00
|
|
|
<Exp: 18 / 108 - 3 * 9>
|
2019-06-28 09:43:17 +00:00
|
|
|
>>> e = Expression.random("{a}*x + {b}*x + 3", ["a>b"], rejected=[0, 1])
|
2019-06-28 07:02:17 +00:00
|
|
|
>>> ee = e.simplify()
|
|
|
|
>>> print(e) # doctest: +SKIP
|
2019-06-28 09:43:17 +00:00
|
|
|
10x - 6x + 3
|
2019-06-28 07:02:17 +00:00
|
|
|
>>> print(ee) # doctest: +SKIP
|
2019-06-28 09:43:17 +00:00
|
|
|
4x + 3
|
2019-05-16 15:21:34 +00:00
|
|
|
|
2018-01-21 09:21:13 +00:00
|
|
|
"""
|
2019-05-16 15:21:34 +00:00
|
|
|
rd_t = Tree.from_str(template, random=True)
|
|
|
|
leafs = extract_rdleaf(rd_t)
|
|
|
|
rd_varia = extract_rv(leafs)
|
|
|
|
generated = random_generator(
|
|
|
|
rd_varia, conditions, rejected, min_max, variables_scope
|
|
|
|
)
|
|
|
|
computed = compute_leafs(leafs, generated)
|
|
|
|
t = replace_rdleaf(rd_t, computed).map_on_leaf(moify)
|
|
|
|
|
|
|
|
if shuffle:
|
|
|
|
raise NotImplemented("Can't suffle expression yet")
|
|
|
|
|
2019-10-13 19:11:05 +00:00
|
|
|
return cls._post_processing(t)
|
2019-05-16 15:21:34 +00:00
|
|
|
|
|
|
|
@classmethod
|
2019-10-13 19:11:05 +00:00
|
|
|
def _post_processing(cls, t):
|
2019-05-16 15:21:34 +00:00
|
|
|
""" Post process the tree by typing it """
|
|
|
|
tt = cls(t)._typing()
|
|
|
|
try:
|
|
|
|
return factory(tt)
|
|
|
|
except TypeError as e:
|
|
|
|
return cls(t)
|
2018-01-21 09:21:13 +00:00
|
|
|
|
2018-09-17 14:29:00 +00:00
|
|
|
def __str__(self):
|
2020-12-15 14:37:27 +00:00
|
|
|
return render(self._tree)
|
2018-09-17 14:29:00 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
2020-12-15 14:37:27 +00:00
|
|
|
return f"<Exp: {render(self._tree, 'txt')}>"
|
2018-09-17 16:18:29 +00:00
|
|
|
|
2018-11-23 09:06:33 +00:00
|
|
|
def _order(self, exclude_nodes=["*", "/", "**"]):
|
|
|
|
""" Order the expression base on types
|
|
|
|
|
|
|
|
:example:
|
2020-12-12 22:14:44 +00:00
|
|
|
|
2018-11-23 09:06:33 +00:00
|
|
|
>>> e = Expression.from_str("1 + 2x + 3 + 4x")
|
|
|
|
>>> print(e)
|
|
|
|
1 + 2x + 3 + 4x
|
|
|
|
>>> #print(e._order())
|
|
|
|
1 + 3 + 2x + 4x
|
|
|
|
>>> e = Expression.from_str("x + 6x^3 + 1 + 2x^2 + 3 + 4x^2 + 5x")
|
|
|
|
>>> print(e._order())
|
|
|
|
x + 5x + 6x^3 + 2x^2 + 4x^2 + 1 + 3
|
|
|
|
"""
|
2019-05-14 04:55:56 +00:00
|
|
|
|
2018-11-23 09:06:33 +00:00
|
|
|
def signature(leaf):
|
|
|
|
try:
|
|
|
|
leaf.node
|
|
|
|
except AttributeError:
|
|
|
|
try:
|
|
|
|
return leaf.signature
|
|
|
|
except AttributeError:
|
|
|
|
return type(leaf)
|
|
|
|
else:
|
|
|
|
try:
|
2020-12-12 22:14:44 +00:00
|
|
|
typed_leaf = typing(
|
|
|
|
leaf.node, leaf.left_value, leaf.right_value)
|
2018-11-23 09:06:33 +00:00
|
|
|
return typed_leaf.signature
|
|
|
|
except (AttributeError, NotImplementedError, TypingError):
|
|
|
|
return type(leaf)
|
|
|
|
|
|
|
|
try:
|
|
|
|
self._tree.node
|
|
|
|
except AttributeError:
|
|
|
|
return self
|
|
|
|
|
2019-05-14 04:55:56 +00:00
|
|
|
organised = AssocialTree.from_any_tree(self._tree).organise_by(
|
|
|
|
signature, recursive=True, exclude_nodes=exclude_nodes
|
|
|
|
)
|
2018-11-23 09:06:33 +00:00
|
|
|
return Expression(organised)
|
|
|
|
|
|
|
|
def _optimize(self, exclude_nodes=["/", "**"]):
|
2018-11-21 07:54:37 +00:00
|
|
|
""" Return a copy of self with an optimize tree
|
2020-12-12 22:14:44 +00:00
|
|
|
|
2018-11-21 08:37:17 +00:00
|
|
|
:example:
|
|
|
|
>>> e = Expression.from_str("2x^2+2x+3x")
|
|
|
|
>>> print(e._tree)
|
|
|
|
+
|
|
|
|
> +
|
|
|
|
| > *
|
|
|
|
| | > 2
|
|
|
|
| | > ^
|
|
|
|
| | | > x
|
|
|
|
| | | > 2
|
|
|
|
| > *
|
|
|
|
| | > 2
|
|
|
|
| | > x
|
|
|
|
> *
|
|
|
|
| > 3
|
|
|
|
| > x
|
|
|
|
>>> print(e._optimize()._tree)
|
|
|
|
+
|
|
|
|
> *
|
|
|
|
| > 2
|
|
|
|
| > ^
|
|
|
|
| | > x
|
|
|
|
| | > 2
|
|
|
|
> +
|
|
|
|
| > *
|
|
|
|
| | > 2
|
|
|
|
| | > x
|
|
|
|
| > *
|
|
|
|
| | > 3
|
|
|
|
| | > x
|
|
|
|
|
2018-11-21 07:54:37 +00:00
|
|
|
"""
|
|
|
|
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
|
2020-12-12 22:14:44 +00:00
|
|
|
|
2018-11-21 07:54:37 +00:00
|
|
|
:example:
|
2018-12-13 08:29:44 +00:00
|
|
|
>>> e = Expression.from_str("2x", typing=False)
|
2018-12-21 10:36:40 +00:00
|
|
|
>>> print(e._tree.map_on_leaf(lambda x: type(x).__name__))
|
2018-11-21 07:54:37 +00:00
|
|
|
*
|
2018-12-21 10:36:40 +00:00
|
|
|
> MOnumber
|
|
|
|
> MOstr
|
2018-11-21 07:54:37 +00:00
|
|
|
>>> typed_e = e._typing()
|
|
|
|
>>> print(type(typed_e._tree))
|
|
|
|
<class 'mapytex.calculus.core.MO.monomial.MOMonomial'>
|
|
|
|
>>> typed_e = e._typing()
|
|
|
|
>>> print(type(typed_e._tree))
|
|
|
|
<class 'mapytex.calculus.core.MO.monomial.MOMonomial'>
|
|
|
|
|
2018-12-13 08:29:44 +00:00
|
|
|
>>> e = Expression.from_str("2x+3+4/5", typing=False)
|
2018-12-21 10:36:40 +00:00
|
|
|
>>> print(e._tree.map_on_leaf(lambda x: type(x).__name__))
|
2018-11-21 07:54:37 +00:00
|
|
|
+
|
|
|
|
> +
|
|
|
|
| > *
|
2018-12-21 10:36:40 +00:00
|
|
|
| | > MOnumber
|
|
|
|
| | > MOstr
|
|
|
|
| > MOnumber
|
2018-11-21 07:54:37 +00:00
|
|
|
> /
|
2018-12-21 10:36:40 +00:00
|
|
|
| > MOnumber
|
|
|
|
| > MOnumber
|
|
|
|
|
2018-11-21 07:54:37 +00:00
|
|
|
>>> typed_e = e._typing()
|
2018-12-21 10:36:40 +00:00
|
|
|
>>> print(e._tree.map_on_leaf(lambda x: type(x).__name__))
|
2018-11-21 07:54:37 +00:00
|
|
|
+
|
2018-12-21 10:36:40 +00:00
|
|
|
> +
|
|
|
|
| > *
|
|
|
|
| | > MOnumber
|
|
|
|
| | > MOstr
|
|
|
|
| > MOnumber
|
|
|
|
> /
|
|
|
|
| > MOnumber
|
|
|
|
| > MOnumber
|
2018-11-21 07:54:37 +00:00
|
|
|
"""
|
|
|
|
try:
|
2018-11-21 14:54:07 +00:00
|
|
|
return Expression(self._tree.apply(typing))
|
|
|
|
except AttributeError:
|
2018-11-21 07:54:37 +00:00
|
|
|
return self
|
|
|
|
|
|
|
|
def _compute(self):
|
|
|
|
"""" Compute one step of self
|
2020-12-12 22:14:44 +00:00
|
|
|
|
2018-11-21 07:54:37 +00:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
return Expression(self._tree.apply_on_last_level(compute))
|
|
|
|
except AttributeError:
|
|
|
|
return self
|
|
|
|
|
|
|
|
def set_ancestor(self, ancestor):
|
|
|
|
""" Set ancestor """
|
|
|
|
self._ancestor = ancestor
|
|
|
|
|
2018-12-07 09:27:50 +00:00
|
|
|
def _simplify(self, optimize=True):
|
2018-09-17 16:18:29 +00:00
|
|
|
""" Compute as much as possible the expression
|
|
|
|
|
2018-10-01 14:14:04 +00:00
|
|
|
:param optimize: bool to optimize tree when it's possible
|
2018-09-17 16:18:29 +00:00
|
|
|
:return: an expression
|
|
|
|
|
|
|
|
:example:
|
|
|
|
>>> e = Expression.from_str("2+3*4")
|
|
|
|
>>> e
|
2018-12-07 09:27:50 +00:00
|
|
|
<Exp: 2 + 3 * 4>
|
|
|
|
>>> f = e._simplify()
|
2018-09-17 16:18:29 +00:00
|
|
|
>>> f
|
|
|
|
<Exp: 14>
|
|
|
|
>>> f._ancestor
|
|
|
|
<Exp: 2 + 12>
|
|
|
|
"""
|
2018-11-21 08:43:35 +00:00
|
|
|
typed_exp = self._typing()
|
|
|
|
|
2018-10-01 14:14:04 +00:00
|
|
|
if optimize:
|
2018-11-23 09:06:33 +00:00
|
|
|
organized_exp = typed_exp._order()
|
|
|
|
opt_exp = organized_exp._optimize()
|
2018-11-21 07:54:37 +00:00
|
|
|
else:
|
2018-11-21 08:43:35 +00:00
|
|
|
opt_exp = typed_exp
|
2018-11-19 11:23:37 +00:00
|
|
|
|
2018-11-21 08:43:35 +00:00
|
|
|
comp_exp = opt_exp._compute()
|
2018-11-21 07:54:37 +00:00
|
|
|
|
|
|
|
if typed_exp == comp_exp:
|
2018-11-21 08:37:17 +00:00
|
|
|
typed_exp.set_ancestor(self._ancestor)
|
|
|
|
return typed_exp
|
2018-09-17 16:18:29 +00:00
|
|
|
else:
|
2018-11-21 07:54:37 +00:00
|
|
|
comp_exp.set_ancestor(self)
|
2018-12-07 09:27:50 +00:00
|
|
|
return comp_exp._simplify(optimize=optimize)
|
|
|
|
|
|
|
|
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 * 4>
|
|
|
|
>>> f = e.simplify()
|
|
|
|
>>> f
|
|
|
|
<Integer 14>
|
|
|
|
>>> f._ancestor
|
|
|
|
<Exp: 2 + 12>
|
|
|
|
"""
|
|
|
|
self._child = self._simplify(optimize=optimize)
|
|
|
|
return factory(self._child, ancestor=self._child._ancestor)
|
2018-09-17 16:18:29 +00:00
|
|
|
|
|
|
|
def explain(self):
|
|
|
|
""" Yield every calculus step which have lead to self
|
2020-12-12 22:14:44 +00:00
|
|
|
|
2018-09-17 16:18:29 +00:00
|
|
|
:example:
|
|
|
|
>>> e = Expression.from_str("2+3*4")
|
|
|
|
>>> f = e.simplify()
|
|
|
|
>>> for s in f.explain():
|
|
|
|
... print(s)
|
|
|
|
2 + 3 * 4
|
|
|
|
2 + 12
|
|
|
|
14
|
2018-10-01 14:14:04 +00:00
|
|
|
>>> 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
|
2018-11-23 09:06:33 +00:00
|
|
|
3 + 7 + 11 + 7 + 17
|
|
|
|
10 + 11 + 24
|
|
|
|
10 + 35
|
2018-10-01 14:14:04 +00:00
|
|
|
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
|
2018-11-23 09:06:33 +00:00
|
|
|
3 + 7 + 30 * 7 * 72
|
|
|
|
10 + 210 * 72
|
2018-10-04 06:16:59 +00:00
|
|
|
10 + 15120
|
2018-10-01 14:14:04 +00:00
|
|
|
15130
|
2018-11-23 09:06:33 +00:00
|
|
|
|
2018-10-01 14:14:04 +00:00
|
|
|
>>> 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
|
2018-10-05 09:18:09 +00:00
|
|
|
>>> 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
|
2018-09-17 16:18:29 +00:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
yield from self._ancestor.explain()
|
|
|
|
except AttributeError:
|
|
|
|
yield self
|
|
|
|
else:
|
|
|
|
yield self
|
2018-01-21 09:21:13 +00:00
|
|
|
|
2019-07-15 16:37:16 +00:00
|
|
|
def __call__(self, value):
|
|
|
|
""" Call a Expression to evaluate itself on value
|
|
|
|
|
2019-07-16 07:30:30 +00:00
|
|
|
:param value: evaluate the Expression with this value
|
|
|
|
:return: Expression simplified if the value is not a string with a length greater than 1.
|
|
|
|
|
2019-07-15 16:37:16 +00:00
|
|
|
:examples:
|
|
|
|
>>> f = Expression.from_str("3*x^2 + 2x + 1")
|
|
|
|
>>> for s in f(2).explain():
|
|
|
|
... print(s)
|
|
|
|
3 * 2^2 + 2 * 2 + 1
|
|
|
|
3 * 4 + 4 + 1
|
|
|
|
12 + 5
|
|
|
|
17
|
2019-07-16 07:30:30 +00:00
|
|
|
>>> f(f(2))
|
|
|
|
<Integer 902>
|
|
|
|
>>> f(17)
|
|
|
|
<Integer 902>
|
|
|
|
>>> f("n")
|
|
|
|
<Quadratic 3n^2 + 2n + 1>
|
|
|
|
>>> f("u_n")
|
|
|
|
<Exp: 3u_n^2 + 2u_n + 1>
|
2019-07-16 07:31:30 +00:00
|
|
|
>>> f(f)
|
2019-10-30 20:12:58 +00:00
|
|
|
<Polynomial 27x^4 + 36x^3 + 36x^2 + 16x + 6>
|
2019-07-15 16:37:16 +00:00
|
|
|
"""
|
|
|
|
tree = self._tree
|
|
|
|
variable = (set(tree.get_leafs(extract_variable)) - {None}).pop()
|
|
|
|
|
2019-07-16 07:30:30 +00:00
|
|
|
try:
|
|
|
|
dest = value._mo
|
|
|
|
except AttributeError:
|
|
|
|
dest = moify(value)
|
2019-07-15 16:37:16 +00:00
|
|
|
replace_var = partial(replace, origin=variable, dest=dest)
|
|
|
|
tree = tree.map_on_leaf(replace_var)
|
|
|
|
|
2019-07-16 07:30:30 +00:00
|
|
|
if isinstance(value, str) and len(value) > 1:
|
|
|
|
return Expression(tree)
|
2019-07-15 16:37:16 +00:00
|
|
|
return Expression(tree).simplify()
|
|
|
|
|
|
|
|
|
|
|
|
def extract_variable(leaf):
|
|
|
|
try:
|
|
|
|
return leaf.variable
|
|
|
|
except AttributeError:
|
|
|
|
return None
|
|
|
|
|
2019-10-13 19:01:31 +00:00
|
|
|
|
2019-07-15 16:37:16 +00:00
|
|
|
def replace(leaf, origin, dest):
|
|
|
|
""" Recursively replace origin to dest in leaf """
|
|
|
|
try:
|
|
|
|
leaf.tree
|
|
|
|
except AttributeError:
|
|
|
|
if leaf == origin:
|
|
|
|
return dest
|
|
|
|
return leaf
|
|
|
|
|
|
|
|
replace_var = partial(replace, origin=origin, dest=dest)
|
|
|
|
return leaf.tree.map_on_leaf(replace_var)
|
2019-05-14 04:55:56 +00:00
|
|
|
|
2019-10-13 19:01:31 +00:00
|
|
|
|
2018-01-21 09:21:13 +00:00
|
|
|
# -----------------------------
|
|
|
|
# Reglages pour 'vim'
|
|
|
|
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
|
|
|
|
# cursor: 16 del
|