Refactor code in directories + add doc strings
This commit is contained in:
parent
1e5113b208
commit
0d9a636da6
8 changed files with 175 additions and 81 deletions
111
python_symb/Expressions/expr.py
Normal file
111
python_symb/Expressions/expr.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import python_symb.MathTypes.symbols
|
||||
from python_symb.Expressions.tree import Tree
|
||||
from python_symb.MathTypes.operator_file import Add, Mul, Min
|
||||
|
||||
from python_symb.MathTypes.operator_file import UnaryOperator, BinOperator, Add, Mul, Min
|
||||
|
||||
from typing import List
|
||||
from python_symb.MathTypes.symbols import Var
|
||||
|
||||
from python_symb.Parsing.parse import infix_str_to_postfix
|
||||
|
||||
|
||||
class Expr(Tree):
|
||||
"""
|
||||
A class to represent an expression tree
|
||||
|
||||
value: the value of the node
|
||||
children: the subtrees of the root (Default : None)
|
||||
|
||||
exemple :
|
||||
5*(2+3) represented as
|
||||
Expr(Mul, [Expr(5), Expr(Expr(Add, [Expr(2), Expr(3)]))])
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, value, children=None):
|
||||
super().__init__(value, children if children else [])
|
||||
|
||||
@staticmethod
|
||||
def from_postfix_list(postfix: List):
|
||||
"""
|
||||
Create an expression tree from a postfix list of tokens
|
||||
|
||||
tokens are : int, float, Var, UnaryOperator, BinOperator
|
||||
|
||||
exemple :
|
||||
x = Var('x')
|
||||
[5, 2, Add] -> Expr(Add, [Expr(5), Expr(2)])
|
||||
"""
|
||||
|
||||
def aux():
|
||||
first = postfix.pop()
|
||||
match first:
|
||||
case int() | Var():
|
||||
return Expr(first)
|
||||
case UnaryOperator():
|
||||
return Expr(first, [aux()])
|
||||
case BinOperator():
|
||||
return Expr(first, [aux(), aux()])
|
||||
|
||||
return aux()
|
||||
|
||||
@staticmethod
|
||||
def from_infix_str(expr_str) -> Expr:
|
||||
"""
|
||||
Create an expression tree from an infix string
|
||||
"""
|
||||
expr_rev_polish = infix_str_to_postfix(expr_str)
|
||||
return Expr.from_postfix_list(expr_rev_polish)
|
||||
|
||||
def bin_op_constructor(self, other, op):
|
||||
"""
|
||||
Construct a binary operation
|
||||
"""
|
||||
match other:
|
||||
case Expr():
|
||||
return Expr(op, [self, other])
|
||||
case Var() | int() | float():
|
||||
return Expr(op, [self, Expr(other)])
|
||||
case _:
|
||||
return ValueError(f'Invalid type for operation: {other} : {type(other)}')
|
||||
|
||||
def __add__(self, other):
|
||||
return self.bin_op_constructor(other, Add)
|
||||
|
||||
def __mul__(self, other):
|
||||
return self.bin_op_constructor(other, Mul)
|
||||
|
||||
def __sub__(self, other):
|
||||
return self.bin_op_constructor(other, Min)
|
||||
|
||||
|
||||
def test1():
|
||||
x, y = Var('x'), Var('y')
|
||||
expr1 = x + y
|
||||
expr2 = 5+x
|
||||
print(expr1 + expr2)
|
||||
|
||||
|
||||
def test2():
|
||||
from python_symb.MathTypes.operator_file import Sin
|
||||
a, b = Var('a'), Var('b')
|
||||
expr = Sin(a+b)
|
||||
print(expr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test1()
|
||||
test2()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
81
python_symb/Expressions/tree.py
Normal file
81
python_symb/Expressions/tree.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
from __future__ import annotations
|
||||
from typing import Iterable, Generator
|
||||
from collections import deque
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class Tree(ABC):
|
||||
"""
|
||||
Ultra generic Test class. Can be used to represent any Test structure.
|
||||
|
||||
:param value : value of the node. Can be a binary operator like "+", a ternary operator like "if", a number etc...
|
||||
|
||||
depth_first_order : the default order of the node in the depth first traversal. Used to implement the depth_first method.
|
||||
0 is pre-order, 1 is in-order (for binary Test), -1 is post-order.
|
||||
for instance to write "a ? b : c" you need to write Tree("?", [Tree("a"), Tree("b"), Tree("c")])
|
||||
and set the depth_first_order of the "?" node to 1.
|
||||
|
||||
children : the children of the node. Can be empty.
|
||||
"""
|
||||
__slots__ = ['value', 'children', 'depth_first_order']
|
||||
|
||||
def __init__(self, value, children: Iterable[Tree] = None, depth_first_order: int = 0):
|
||||
self.value = value
|
||||
self.depth_first_order = depth_first_order
|
||||
self.children = children if children else []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Tree({self.value}, {self.children})' if self.children else f'Tree({self.value})'
|
||||
|
||||
def height(self) -> int:
|
||||
return 1 + max((child.height() for child in self.children), default=0)
|
||||
|
||||
def size(self) -> int:
|
||||
return 1 + sum(child.size() for child in self.children)
|
||||
|
||||
def breadth_first(self) -> Generator[Tree]:
|
||||
|
||||
queue = deque([self])
|
||||
|
||||
while queue:
|
||||
poped = queue.popleft()
|
||||
for child in poped.children:
|
||||
queue.append(child)
|
||||
|
||||
yield poped
|
||||
|
||||
def depth_first_default(self) -> Generator[Tree]:
|
||||
|
||||
def aux(tree):
|
||||
n = len(tree.children)
|
||||
if not tree.children:
|
||||
yield tree
|
||||
|
||||
for i, child in enumerate(tree.children):
|
||||
if i == tree.depth_first_order:
|
||||
yield tree
|
||||
|
||||
yield from aux(child)
|
||||
|
||||
if tree.depth_first_order == -1:
|
||||
yield tree
|
||||
|
||||
yield from aux(self)
|
||||
|
||||
def depth_first_pre_order(self) -> Generator[Tree]:
|
||||
|
||||
def aux(tree):
|
||||
yield tree
|
||||
for child in tree.children:
|
||||
yield from aux(child)
|
||||
|
||||
yield from aux(self)
|
||||
|
||||
def depth_first_post_order(self) -> Generator[Tree]:
|
||||
|
||||
def aux(tree):
|
||||
for child in tree.children:
|
||||
yield from aux(child)
|
||||
yield tree
|
||||
|
||||
yield from aux(self)
|
Loading…
Add table
Add a link
Reference in a new issue