forked from okuoku/xitomatl
-
Notifications
You must be signed in to change notification settings - Fork 1
/
logic.sls
233 lines (192 loc) · 7.38 KB
/
logic.sls
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#!r6rs
;; Copyright 2012 Derick Eddington. My MIT-style license is in the file named
;; LICENSE from the original collection this file is distributed with.
; TODO: Tests that demonstrate goal-creating expressions being evaluated more
; than once.
; TODO: Investigate redesigning so that goal-creating expressions are evaluated
; only once.
; TODO: Investigate how and when, maybe the default, to have interleaving of
; solutions of sub-goals. Currently disj yields all the solutions of a sub-goal
; before those of following sub-goals, but this is problematic especially when a
; sub-goal yields infinite amount of solutions.
; TODO: Statistics collection of execution complexity, disabled by default.
(library (xitomatl logic)
(export
? vars var?
solve solve*
== conj disj #;disj/while #;disj/until neg
escape/deref debug-vars
deref deref*)
(import
(rnrs base)
(rnrs control)
(rnrs lists)
(rnrs io simple)
(rnrs records syntactic)
(rnrs hashtables)
(xitomatl common)
(xitomatl logic stream))
(define-record-type var (fields name))
(define-syntax vars
(syntax-rules ()
((_ (v ...) . body)
(let ((v (make-var 'v)) ...) . body))))
(define ? (make-var '?))
(define (maybe-? v)
(cond ((pair? v) (cons (maybe-? (car v)) (maybe-? (cdr v))))
((eq? ? v) (make-var '?))
(else v)))
(define (empty-knowledge) '())
(define (extend v x know)
(assert (var? v))
(cons (cons v x) know))
(define (deref x know)
(cond ((and (var? x) (assq x know)) => (lambda (p) (deref (cdr p) know)))
(else x)))
(define (unify a b know)
(let ((a (deref a know)) (b (deref b know)))
(cond ((eq? a b) know)
((var? a) (extend (maybe-? a) (maybe-? b) know))
((var? b) (extend (maybe-? b) (maybe-? a) know))
((and (pair? a) (pair? b))
(let ((k (unify (car a) (car b) know)))
(and k (unify (cdr a) (cdr b) k))))
((equal? a b) know)
(else #F))))
;;;; Core relations ---------------------------------------------------------
(define (== a b)
(lambda (know)
(delay (cond ((unify a b know) => list)
(else '())))))
(define (debug-vars . vars)
(lambda (know)
(delay
(begin (display "--- debug vars ----------------------------------------------\n")
(for-each (lambda (v)
(when (var? v)
(let ((d (deref* v know)))
(if (eq? v d)
(printf "~a not bound\n" (var-name v))
(printf "~a = ~s\n" (var-name v) d)))))
vars)
(display "-------------------------------------------------------------\n")
(list know)))))
(define-syntax define-goal-streamer
(syntax-rules ()
((_ id id*)
(define-syntax id
(syntax-rules ()
((_ goals (... ...))
(id* (fixed-stream goals (... ...)))))))))
(define-goal-streamer conj conj*)
(define (conj* stream-of-goals)
; TODO: Maybe better to use some stream-fold passing knowledge-stream as seed?
(lambda (know)
(let ((g (force stream-of-goals)))
(if (null? g)
(list know) ; success
(stream-flatten
(stream-map (conj* (cdr g))
((car g) know)))))))
(define (disj/control* pred stream-of-goals)
(lambda (know)
(stream-flatten
(stream-map-while (lambda (g)
(let ((ks (force (g know))))
(values (pred ks) ks)))
stream-of-goals))))
(define-goal-streamer disj disj*)
(define (disj* stream-of-goals)
(disj/control* (lambda (ks) #T) stream-of-goals))
#;(define-goal-streamer disj/while disj/while*) ; TODO: How to describe?
#;(define (disj/while* stream-of-goals)
(disj/control* (lambda (ks) (not (null? ks))) stream-of-goals))
#;(define-goal-streamer disj/until disj/until*) ; Short-circuit Or
#;(define (disj/until* stream-of-goals)
(disj/control* null? stream-of-goals))
(define (neg* goal-promise)
(lambda (know)
(let* ((goal (force goal-promise))
(ks (goal know)))
(if (null? (force ks))
(list know) ; success
'())))) ; failure
(define-syntax neg
(syntax-rules ()
((_ goal)
(neg* (delay goal)))))
(define-syntax escape/deref
; TODO? Maybe this can participate in the reducing of goal-procedure creation?
(syntax-rules ()
((_ (v ...) . body)
(lambda (know)
((let ((v (deref v know)) ...) . body) ; Returns a goal to apply.
know)))))
;;;; Solving ----------------------------------------------------------------
(define (deref* x know)
; Replace embedded variables with their values.
(let ((x (deref x know)))
(if (pair? x)
(cons (deref* (car x) know)
(deref* (cdr x) know))
x)))
(define (reify-var-names know)
; Create a mapping of logic variables in a knowledge to reified names.
(define (rn v i)
(string->symbol (string-append (symbol->string (var-name v)) "." (number->string i))))
(define ht (make-eq-hashtable))
(let recur ((t know) (i 0))
(cond ((pair? t)
(recur (car t) (recur (cdr t) i)))
((and (var? t)
(not (hashtable-contains? ht t)))
(hashtable-set! ht t (rn t i))
(+ 1 i))
(else i)))
ht)
(define (solve** limit for goal)
; Start the initial goal, and run until the amount of solutions reaches the
; limit.
(define knows (stream->list (goal (empty-knowledge)) limit))
(if (eq? 'raw for)
knows
(map (lambda (know)
; Make the returned solutions more aesthetic.
(define rm (reify-var-names know))
(define (r t)
(cond ((pair? t)
(cons (r (car t)) (r (cdr t))))
((var? t)
(or (and (or (eq? 'show for)
(not (memq t for)))
(hashtable-ref rm t #F))
(var-name t)))
(else t)))
`(solution .
,(map (lambda (p) `(,(r (car p)) = ,(r (cdr p))))
(if (eq? 'show for)
; Return everything in the knowledge, with reified variable names,
; without replacing embedded variables with their values.
know
; Return only what is requested, with reified variable names, with
; values replaced for embedded variables.
(fold-right (lambda (f a)
(let ((f* (deref* f know)))
(if (eq? f f*) a
(cons (cons f f*) a))))
'() for)))))
knows)))
(define-syntax solve*
(syntax-rules (show raw)
((_ limit (for ...) goal)
(vars (for ...)
(solve** limit (list for ...) goal)))
((_ limit show goal)
(solve** limit 'show goal))
((_ limit raw goal)
(solve** limit 'raw goal))))
(define-syntax solve
(syntax-rules ()
((_ . r)
(solve* +inf.0 . r))))
)