Skip to content

Instantly share code, notes, and snippets.

@asukaminato0721
Last active April 29, 2023 16:52
Show Gist options
  • Save asukaminato0721/1bb2dbcdfc4fcba243f0eb9bc3dc0b0a to your computer and use it in GitHub Desktop.
Save asukaminato0721/1bb2dbcdfc4fcba243f0eb9bc3dc0b0a to your computer and use it in GitHub Desktop.
from __future__ import annotations
from dataclasses import dataclass
from typing import Callable, List
from operator import attrgetter, add, sub, mul, truediv
def adder(a: Connector, b: Connector, c: Connector):
return TernaryConstraint(a, b, c, add, sub, sub)
def multiplier(a: Connector, b: Connector, c: Connector):
"""The constraint that a * b = c."""
return TernaryConstraint(a, b, c, mul, truediv, truediv)
def converter(c: Connector, f: Connector):
"""Connect c to f with constraints to convert from Celsius to Fahrenheit."""
u, v, w, x, y = [Connector() for _ in range(5)]
multiplier(c, w, u)
multiplier(v, x, u)
adder(v, y, f)
w.set_val("", 9)
x.set_val("", 5)
y.set_val("", 32)
class Connector:
"""A connector between constraints."""
def __init__(self, name: str = None):
self.informant = None
self.constraints: List[TernaryConstraint] = []
self.val = None
self.name = name
def set_val(self, source: str, value: float):
if self.val is None:
self.informant, self.val = source, value
if self.name is not None:
print(self.name, "=", value)
Connector.inform_all_except(source, attrgetter("new_val"), self.constraints)
elif self.val != value:
print("Contradiction detected:", self.val, "vs", value)
def forget(self, source):
if self.informant == source:
self.informant, self.val = None, None
if self.name is not None:
print(self.name, "is forgotten")
Connector.inform_all_except(source, attrgetter("forget"), self.constraints)
def has_val(self):
return self.val is not None
def connect(self, source):
self.constraints.append(source)
@staticmethod
def inform_all_except(
source: str, message: Callable, constraints: List[TernaryConstraint]
):
"""Inform all constraints of the message, except source."""
for c in constraints:
if c != source:
message(c)()
@dataclass
class TernaryConstraint:
"""The constraint that ab(a,b)=c and ca(c,a)=b and cb(c,b) = a."""
a: Connector
b: Connector
c: Connector
ab: Callable[[float, float], float]
ca: Callable[[float, float], float]
cb: Callable[[float, float], float]
def __post_init__(self):
for i in (self.a, self.b, self.c):
i.connect(self)
def new_val(self):
av, bv, cv = [connector.has_val() for connector in (self.a, self.b, self.c)]
if av and bv:
self.c.set_val(self, self.ab(self.a.val, self.b.val))
elif av and cv:
self.b.set_val(self, self.ca(self.c.val, self.a.val))
elif bv and cv:
self.a.set_val(self, self.cb(self.c.val, self.b.val))
def forget(self):
for connector in (self.a, self.b, self.c):
connector.forget(self)
celsius = Connector("Celsius")
fahrenheit = Connector("Fahrenheit")
converter(celsius, fahrenheit)
celsius.set_val("user", 25)
fahrenheit.set_val("user", 212)
celsius.forget("user")
fahrenheit.set_val("user", 212)
fahrenheit.set_val("user", 100)
fahrenheit.forget("user")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment