Create contains, eq and replace operations

dev
Bram van den Heuvel 2026-07-12 16:05:26 +02:00
parent dcad494c7d
commit 66beff5792
1 changed files with 74 additions and 8 deletions

View File

@ -4,7 +4,7 @@
"""
from __future__ import annotations
from typing import Callable
from typing import Callable, Generator
import copy
@ -24,6 +24,24 @@ USABLE_VAR_NAMES = [
'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω'
]
def contains(haystack : Term, needle : Term) -> bool:
"""
Determine whether a given small term value is contained in a bigger
term.
:param haystack: The term to search through.
:type haystack: Term
:param needle: The term to find.
:type needle: Term
:return: Whether the needle was found in the haystack.
:rtype: bool
"""
for term in haystack.rec_iter():
if term == needle:
return True
else:
return False
def normalize(term : Term) -> Term:
"""
Create a normalization function. This function simplifies functions as
@ -78,6 +96,21 @@ def std_norm_iter(term : Term) -> Term:
return term
_known_norms.append(std_norm_iter)
def replace(term : Term, old : Term, new : Term) -> Term:
"""
Find and replace occurrences of a sub-term in a given term.
:param term: The original term to update.
:type term: Term
:param old: The term to find and replace.
:type old: Term
:param new: The new term to replace the old one with.
:type new: Term
:return: The replaced term result.
:rtype: Term
"""
return traverse_bottom_up(f=lambda t : new if t == old else t, term=term)
def traverse(f : Callable[[Term], Term], term : Term) -> Term:
"""
Traverse through the tree back-and-forth, aiming to run the given
@ -96,7 +129,7 @@ def traverse(f : Callable[[Term], Term], term : Term) -> Term:
a, b = Term(), term
while a != b:
a, b = b, traverse_bottom_up(f=f, term=traverse_top_down(f=f, term=term))
a, b = b, traverse_bottom_up(f=f, term=traverse_top_down(f=f, term=b))
return b
@ -171,12 +204,6 @@ class Term:
Base class for all terms.
"""
def __eq__(self, other) -> bool:
"""
Determine whether two terms are equal.
"""
return str(self) == str(other)
def __repr__(self) -> str:
"""
Create a representation of a term.
@ -186,6 +213,12 @@ class Term:
def normalize(self) -> Term:
return normalize(term=self)
def replace(self, old : Term, new : Term) -> Term:
return replace(term=self, old=old, new=new)
def rec_iter(self) -> Generator[Term, None, None]:
yield self
def to_str(self, vars : dict[str, Var]) -> str:
return "<undefined>"
@ -205,6 +238,11 @@ class App(Term):
"""
return self.to_str(vars={})
def rec_iter(self) -> Generator[Term, None, None]:
yield self
yield from self.f.rec_iter()
yield from self.x.rec_iter()
def to_str(self, vars: dict[str, Var]) -> str:
f_str = self.f.to_str(vars=vars)
x_str = self.x.to_str(vars=vars)
@ -246,6 +284,18 @@ class Lambda(Term):
out : Term
var : Var
def __eq__(self, value: object) -> bool:
if not isinstance(value, Lambda):
return False
for c in USABLE_VAR_NAMES:
if not contains(self.out, Const(c)):
return self.substitute(Const(c)) == value.substitute((Const(c)))
else:
raise ValueError(
"Ran out of usable variables for comparing lambda structures"
)
def __init__(self, f : Callable[[Var], Term]) -> None:
self.var = Var()
self.out = f(self.var)
@ -256,6 +306,10 @@ class Lambda(Term):
"""
return self.to_str(vars={})
def rec_iter(self) -> Generator[Term, None, None]:
yield self
yield from self.out.rec_iter()
def replace_content(self, term : Term) -> Lambda:
"""
Replace the content of the lambda function with the given term.
@ -427,3 +481,15 @@ if __name__ == "__main__":
g = L1(lambda b : A2(b, bool_f, bool_t))
print(g)
print(g.normalize())
print("\nFIND & REPLACE")
print(30 * "=")
h = A3(Const("f"), Const("x"), Const("y"), Const("z"))
i = A3(Const("f"), Const("x"), Const("b"), Const("z")).replace(
old=Const("b"), new=Const("y"),
)
print(h)
print(i)
assert h == i