Create AssocialTree and method to init tree from a node and a list of
leafs
This commit is contained in:
parent
8b201fa8d0
commit
935a0d0d23
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user