-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunification.py
142 lines (99 loc) · 3.43 KB
/
unification.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class Unify:
def __init__(self):
self.reference = {} # variable bindings
self.checkpoint = [] # unification checkpoints
self.var_ctx = 0 # unique variable id
def variable(self, *args):
self.var_ctx += 1
return ['%s_%d' % (var, self.var_ctx) for var in args]
def __call__(self, var_x, var_y):
# resolve variable X
while isinstance(var_x, str) and var_x in self.reference:
var_x = self.reference[var_x]
# resolve variable Y
while isinstance(var_y, str) and var_y in self.reference:
var_y = self.reference[var_y]
# unified to self?
if isinstance(var_x, str) and isinstance(var_y, str):
if var_x == var_y:
return True
# unify free variable X
if isinstance(var_x, str):
self.reference[var_x] = var_y
self.checkpoint[-1].append(var_x)
return True
# unify free variable Y
if isinstance(var_y, str):
self.reference[var_y] = var_x
self.checkpoint[-1].append(var_y)
return True
# tuple is unified element-wise
if isinstance(var_x, tuple) and isinstance(var_y, tuple):
if len(var_x) == len(var_y):
return all(self(i, j) for i, j in zip(var_x, var_y))
# atom is unified on equality
if isinstance(var_x, int) and isinstance(var_y, int):
if var_x == var_y:
return True
# not unifiable
raise KeyError()
def __getitem__(self, var):
# resolve tuple by members
if isinstance(var, tuple):
return tuple(self[i] for i in var)
# resolve variable recursively
if isinstance(var, str):
if var in self.reference:
return self[self.reference[var]]
return var
# atomic value
if isinstance(var, int):
return var
# invalid object
raise TypeError()
def __enter__(self):
# store unification checkpoint
self.checkpoint.append([])
def __exit__(self, exc_type, *args):
# remove checkpoint and unbind variables
for var in self.checkpoint.pop():
if var in self.reference:
del self.reference[var]
# suppress exception
if exc_type is not GeneratorExit:
return True
def conc(PREFIX, SUFFIX, RESULT):
HEAD, TAIL, CONC = unify.variable('HEAD', 'TAIL', 'CONC')
with unify:
unify(PREFIX, EMPTY) and unify(SUFFIX, RESULT)
yield
with unify:
unify(PREFIX, (HEAD, TAIL)) and unify(RESULT, (HEAD, CONC))
yield from conc(TAIL, SUFFIX, CONC)
#RUN
unify = Unify()
EMPTY = tuple()
prefix = (1, (2, EMPTY))
suffix = (3, (4, EMPTY))
result = (1, (2, (3, (4, EMPTY))))
for _ in conc(prefix, suffix, 'RESULT'):
print(unify['RESULT'])
for _ in conc(prefix, 'SUFFIX', result):
print(unify['SUFFIX'])
for _ in conc('PREFIX', 'SUFFIX', result):
print('possible answer is', unify['PREFIX'], 'and', unify['SUFFIX'])
for _ in conc(prefix, suffix, result):
print('yes')
break
else:
print('no')
for _ in conc(prefix, suffix, result):
print('yes')
break
else:
print('no')
for _ in zip(range(5), conc('PREFIX', 'SUFFIX', 'RESULT')):
print('PREFIX =', unify['PREFIX'])
print('SUFFIX =', unify['SUFFIX'])
print('RESULT =', unify['RESULT'])
print()