def __init__(self, subs=Map()): if isinstance(subs, dict): self.subs = Map() for k, v in subs.items(): self.subs = self.subs.insert(k, v) else: self.subs = subs
def test_map_iterates_over_all_association(): items = {("one", 1), ("two", 2)} mapping = Map() for key, value in items: mapping = mapping.insert(key, value) mapping_iter = iter(mapping) assert set(mapping_iter) == items
def test_map_insert_does_not_modify_old_map(): mapping1 = Map().insert("one", 1) mapping1_backup = copy.deepcopy(mapping1) mapping2 = mapping1.insert("two", 2) assert mapping2 is not mapping1 items_a = set(mapping1) items_b = set(mapping1_backup) assert items_a == items_b
def test_remove_item(): mapping0 = Map() mapping1 = mapping0.insert("one", 1) mapping2 = mapping1.insert("two", 2) mapping3 = mapping2.insert("three", 3) mapping = mapping3.remove("two") assert set(mapping) == {("one", 1), ("three", 3)} assert set(mapping3) == {("one", 1), ("two", 2), ("three", 3)} assert set(mapping2) == {("one", 1), ("two", 2)} assert set(mapping1) == {("one", 1)} assert set(mapping0) == set()
def test_map_deepcopy_returns_different_object_with_same_items(): map0 = Map().insert("key", "value") map1 = copy.deepcopy(map0) assert map0 is not map1 items_a = set(map0) items_b = set(map1) assert items_a == items_b
def test_all_empty_maps_share_an_instance(): assert Map() is Map()
def test_empty_map_supports_iteration(): assert list(Map()) == []
def test_empty_mapping(): assert Map().is_empty()
def test_map_is_immutable(): mapping = Map().insert("one", 1) with raises(TypeError): mapping.key = 0 with raises(TypeError): mapping[0] = 0
def test_can_look_up_a_value_after_inserting_another(): mapping = Map() mapping = mapping.insert("one", 1) mapping = mapping.insert("two", 2) assert mapping.lookup("two") == 2 assert mapping.lookup("one") == 1
def test_looking_up_existing_key_returns_value(): mapping = Map().insert("key", "value") val = mapping.lookup("key") assert val == "value"
def test_looking_up_in_the_empty_map_raises(): mapping = Map() with raises(KeyError): mapping.lookup("key")
def test_empty_map_deepcopy_returns_same_object(): map0 = Map() map1 = copy.deepcopy(map0) assert map0 is map1
def test_map_insert_returns_map(): mapping = Map().insert("key", "value") assert isinstance(mapping, Map)
def test_remove_nonexisting_key_raises(): mapping = Map().insert("key", "value") with raises(KeyError): mapping.remove("foo")
def test_map_with_elements_inserted_is_not_empty(): mapping = Map().insert("key", "value") assert not mapping.is_empty()
def test_walk_deep_map(): w, x, y, z = variables("w, x, y, z") sub = Substitution({x: "b", z: y, w: Map().insert(x, z).insert("a", "b")}) value = sub.walk_deep(w) assert value == Map().insert("b", y).insert("a", "b")
class Substitution: def __init__(self, subs=Map()): if isinstance(subs, dict): self.subs = Map() for k, v in subs.items(): self.subs = self.subs.insert(k, v) else: self.subs = subs @staticmethod def is_valid(): return True def walk(self, var): try: value = self.subs.lookup(var) except KeyError: return var return self.walk_var(value) def walk_var(self, value): if is_var(value): return self.walk(value) return value def walk_deep(self, x): x = self.walk_var(x) if is_var(x) or is_atom(x): return x if hasattr(x, '__map__'): return x.__map__(self.walk_deep) elif isinstance(x, (list, tuple, set)): return type(x)(self.walk_deep(item) for item in x) elif isinstance(x, dict): return {self.walk_deep(k): self.walk_deep(v) for k, v in x.items()} else: raise NotImplementedError("walk_deep: {}".format(type(x))) def extend(self, var, value): if self.occurs(var, value): return InvalidSubstitution() return Substitution(self.subs.insert(var, value)) def occurs(self, var, value): value = self.walk_var(value) if is_var(value): return var == value elif is_atom(value): return False iterator = as_iterable(value) if iterator: return any(self.occurs(var, v) for v in iterator) return False def unify(self, x, y, strict=True): x = self.walk_var(x) y = self.walk_var(y) if x == y: return self elif hasattr(x, '__unify__'): return x.__unify__(y, self) elif hasattr(y, '__unify__'): return y.__unify__(x, self) elif is_atom(x) or is_atom(y): return InvalidSubstitution() if strict and type(x) != type(y): return InvalidSubstitution() if len(x) != len(y): return InvalidSubstitution() x_iter = as_iterable(x) y_iter = as_iterable(y) if x_iter and y_iter: for xi, yi in zip(x_iter, y_iter): self = self.unify(xi, yi) return self return InvalidSubstitution() def reify(self, v): v = self.walk_deep(v) r = Substitution()._reify(v) return r.walk_deep(v) def _reify(self, v): v = self.walk_var(v) if is_var(v): n = len(self.subs) rv = ReifiedVariable(n) return Substitution(self.subs.insert(v, rv)) elif is_atom(v): return self r = self for item in as_iterable(v): r = r._reify(item) return r def __eq__(self, other): return isinstance(other, Substitution) and dict(self.subs) == dict(other.subs) def __hash__(self): return hash(self.subs) def __repr__(self): return 'Substitution({})'.format(self.subs)