Create AssocialTree and method to init tree from a node and a list of

leafs
This commit is contained in:
Bertrand Benjamin 2018-01-30 05:34:18 +03:00
parent 8b201fa8d0
commit 935a0d0d23
1 changed files with 210 additions and 26 deletions

View File

@ -78,7 +78,7 @@ class Tree(object):
> 4
"""
t = MutableTree.from_str(expression)
return cls.from_mutable_tree(t)
return cls.from_any_tree(t)
@classmethod
def from_nested_parenthesis(cls, nested_parenthesis):
@ -120,29 +120,86 @@ class Tree(object):
node = nested_parenthesis[0]
try:
left_value = Tree.from_nested_parenthesis(nested_parenthesis[1][0])
left_value = cls.from_nested_parenthesis(nested_parenthesis[1][0])
except ValueError:
left_value = nested_parenthesis[1][0]
try:
right_value = Tree.from_nested_parenthesis(nested_parenthesis[1][1])
right_value = cls.from_nested_parenthesis(nested_parenthesis[1][1])
except ValueError:
right_value = nested_parenthesis[1][1]
return cls(node, left_value, right_value)
@classmethod
def from_mutable_tree(cls, mutable_tree):
""" Initial a Tree from a MutableTree
def from_list(cls, node, leafs):
""" Initiate a balanced tree with one node and a list of leafs
:param node: node for all node of the tree
:param leafs: list of leafs
:example:
>>> t = Tree.from_list("+", [1, 2])
>>> print(t)
+
> 1
> 2
>>> t = Tree.from_list("+", [1, 2, 3])
>>> print(t)
+
> 1
> +
| > 2
| > 3
>>> t = Tree.from_list("+", [1, 2, 3, 4])
>>> print(t)
+
> +
| > 1
| > 2
> +
| > 3
| > 4
>>> t = Tree.from_list("+", [1, 2])
>>> t2 = Tree.from_list("*", [1, t])
>>> print(t2)
*
> 1
> +
| > 1
| > 2
"""
len_leafs = len(leafs)
if len_leafs < 2:
raise ValueError(f"Not enough leafs. Need at least 2 got {len(leafs)}")
elif len_leafs == 2:
l_value = leafs[0]
r_value = leafs[1]
elif len_leafs == 3:
l_value = leafs[0]
r_value = cls.from_list(node, leafs[1:])
else:
l_value = cls.from_list(node, leafs[:len_leafs//2])
r_value = cls.from_list(node, leafs[len_leafs//2:])
return cls(node, l_value, r_value)
@classmethod
def from_any_tree(cls, tree):
""" Initial a Tree from an other type of tree (except LeafTree)
It also work to initiate MutableTree, AssocialTree or LeafTree from
any tree.
:example:
>>> t = MutableTree("*", 1, 2)
>>> print(Tree.from_mutable_tree(t))
>>> print(Tree.from_any_tree(t))
*
> 1
> 2
>>> t1 = MutableTree("*", 1, 2)
>>> t2 = MutableTree("*", t1, 3)
>>> print(Tree.from_mutable_tree(t2))
>>> print(Tree.from_any_tree(t2))
*
> *
| > 1
@ -153,15 +210,22 @@ class Tree(object):
*
> 1
> None
>>> Tree.from_mutable_tree(t)
>>> Tree.from_any_tree(t)
Traceback (most recent call last):
...
TypeError: Tree can't have empty node or leaf
>>> tl = LeafTree("/", 1, 4)
>>> t2 = MutableTree("*", tl, 3)
>>> t = Tree.from_any_tree(t2)
>>> type(t)
<class 'mapytex.calculus.core.tree.Tree'>
>>> type(t.left_value)
<class 'mapytex.calculus.core.tree.LeafTree'>
"""
node = mutable_tree.node
left_value = mutable_tree.left_value
right_value = mutable_tree.right_value
node = tree.node
left_value = tree.left_value
right_value = tree.right_value
if node is None or \
left_value is None or \
@ -169,13 +233,23 @@ class Tree(object):
raise TypeError("Tree can't have empty node or leaf")
try:
l_value = Tree.from_mutable_tree(left_value)
left_value.IMLEAF
except AttributeError:
try:
l_value = cls.from_any_tree(left_value)
except AttributeError:
l_value = left_value
else:
l_value = left_value
try:
r_value = Tree.from_mutable_tree(right_value)
right_value.IMLEAF
except AttributeError:
try:
r_value = cls.from_any_tree(right_value)
except AttributeError:
r_value = right_value
else:
r_value = right_value
return cls(node, l_value, r_value)
@ -188,10 +262,7 @@ class Tree(object):
:example:
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t = Tree.from_str("3*4+2")
>>> print(t)
+
> *
@ -227,10 +298,7 @@ class Tree(object):
:example:
>>> nested_par = ("+", (
... ("*", (3, 4)),
... 2))
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t = Tree.from_str("3*4+2")
>>> print(t)
+
> *
@ -286,6 +354,7 @@ class Tree(object):
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.apply(to_nested)
('+', (1, 2))
>>> assert t.apply(to_nested) == nested_par
>>> nested_par = ("+", (
... ("*", (3, 4)),
@ -293,6 +362,7 @@ class Tree(object):
>>> t = Tree.from_nested_parenthesis(nested_par)
>>> t.apply(to_nested)
('+', (('*', (3, 4)), 2))
>>> assert t.apply(to_nested) == nested_par
>>> from .evaluate import compute
>>> t.apply(compute)
@ -319,9 +389,9 @@ class Tree(object):
:example:
>>> t = Tree.from_str("3*4+2")
>>> t = Tree.from_str("3+4+5*2")
>>> [l for l in t.get_leafs()]
[3, 4, 2]
[3, 4, 5, 2]
>>> {type(l) for l in t.get_leafs()}
{<class 'int'>}
"""
@ -475,8 +545,6 @@ class Tree(object):
"""
return self.apply(postfix_concatenate)
class MutableTree(Tree):
"""
@ -776,11 +844,127 @@ class LeafTree(Tree):
- apply_on_last_level
"""
IMLEAF = 1
def apply(self, *args):
raise AttributeError("Can't use apply on a LeafTree")
def apply_on_last_level(self, *args):
raise AttributeError("Can't use apply on a LeafTree")
raise AttributeError("Can't use apply_on_last_level on a LeafTree")
class AssocialTree(Tree):
""" Tree which concider every subtree with a node different from itself
as a Lead
"""
def map_on_leaf(self, function):
""" Map on leafs a function
:param function: function on a single value or a tree
:returns: Tree with calculated leaf
:example:
>>> t = Tree.from_str("3*4+2")
>>> print(t)
+
> *
| > 3
| > 4
> 2
>>> print(t.map_on_leaf(lambda x:2*x))
+
> *
| > 6
| > 8
> 4
"""
try:
left_applied = self.left_value.map_on_leaf(function)
except AttributeError:
left_applied = function(self.left_value)
try:
right_applied = self.right_value.map_on_leaf(function)
except AttributeError:
right_applied = function(self.right_value)
return Tree(self.node, left_applied, right_applied)
def apply_on_last_level(self, function):
raise AttributeError("apply_on_last_level not available for AssocialTree")
def apply(self, function):
""" Apply the function on every node of the tree
:param function: (op, a, a) -> b
:returns: b
:example:
>>> def to_nested(op, left, right):
... try:
... l = f"tree({left.node})"
... except AttributeError:
... l = left
... try:
... r = f"tree({right.node})"
... except AttributeError:
... r = right
... return (op, (l, r))
>>> t = AssocialTree.from_str("3+4+5*2")
>>> t.apply(to_nested)
('+', (('+', (3, 4)), 'tree(*)'))
"""
try:
if self.left_value.node == self.node:
left_value = self.left_value.apply(function)
else:
left_value = self.left_value
except AttributeError:
left_value = self.left_value
try:
if self.right_value.node == self.node:
right_value = self.right_value.apply(function)
else:
right_value = self.right_value
except AttributeError:
right_value = self.right_value
return function(self.node, left_value, right_value)
def get_leafs(self, callback = lambda x:x):
""" Generator which yield all the leaf value of the tree.
Callback act on every leaf.
:param callback: function on leaf or Tree
:example:
>>> t = AssocialTree.from_str("3+4+5*2")
>>> [l for l in t.get_leafs(str)]
['3', '4', '*\\n > 5\\n > 2']
>>> [ l for l in t.get_leafs(type) ]
[<class 'int'>, <class 'int'>, <class 'mapytex.calculus.core.tree.AssocialTree'>]
"""
try:
if self.left_value.node == self.node:
yield from self.left_value.get_leafs(callback)
else:
yield callback(self.left_value)
except AttributeError:
yield callback(self.left_value)
try:
if self.right_value.node == self.node:
yield from self.right_value.get_leafs(callback)
else:
yield callback(self.right_value)
except AttributeError:
yield callback(self.right_value)
# -----------------------------
# Reglages pour 'vim'