-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathclass06_bfs_scheme.scm
More file actions
384 lines (336 loc) · 11 KB
/
class06_bfs_scheme.scm
File metadata and controls
384 lines (336 loc) · 11 KB
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
;; Recursive Breadth-First Search
;;
;; An Implementation in Scheme for Undirected Graphs
;;
;; Taylor Alexander Brown
;; Oregon State University
;; CS 446: Networks in Computational Biology
;; Fall 2017
;; Define Some Useful Constants.
(define white 'white)
(define gray 'gray)
(define black 'black)
(define nil '())
(define infinity 'infinity)
;; Return an uninitialized graph data structure given an adjacency list
;; by adding color, predecessor, and distance attributes
;; and setting them to nil values.
(define (adjlist->graph adjacency-list)
(map (lambda (vertex)
(list (car vertex) nil nil nil (cadr vertex)))
adjacency-list))
;; Graph 1
;;
;; A----C
;; | \
;; | \
;; B----D
;;
;; Define the first adjacency list.
(define adjlist1 '((A (B C D))
(B (A D))
(C (A))
(D (A B))))
;; Create its uninitialized graph representation.
(define graph1 (adjlist->graph adjlist1))
;; Interpreter Input/Output:
;;
;; > graph1
;; ((a () () () (b c d))
;; (b () () () (a d))
;; (c () () () (a))
;; (d () () () (a b)))
;; Graph 2
;;
;; From Cormen, et. al., p. 596
;;
;; R----S T----U
;; | | / | / |
;; | | / | / |
;; V W----X----Y
;;
;; Define the second adjacency list.
(define adjlist2 '((S (R W))
(R (S V))
(T (U W X))
(U (T X Y))
(V (R))
(W (S T X))
(X (T U W Y))
(Y (U X))))
;; Create its uninitialized graph representation.
(define graph2 (adjlist->graph adjlist2))
;; Interpreter Input/Output:
;;
;; > graph2
;; ((s () () () (r w))
;; (r () () () (s v))
;; (t () () () (u w x))
;; (u () () () (t x y))
;; (v () () () (r))
;; (w () () () (s t x))
;; (x () () () (t u w y))
;; (y () () () (u x)))
;; Define Graph Vertex Accessors.
(define (vertex-of vertex)
(list-ref vertex 0))
(define (color-of vertex)
(list-ref vertex 1))
(define (predecessor-of vertex)
(list-ref vertex 2))
(define (distance-of vertex)
(list-ref vertex 3))
(define (neighbors-of vertex graph)
(map (lambda (neighbor)
(assq neighbor graph))
(list-ref vertex 4)))
;; Define Graph Vertex Mutators.
(define (set-color! vertex color)
(set-car! (cdr vertex) color))
(define (set-predecessor! vertex predecessor)
(set-car! (cddr vertex) predecessor))
(define (set-distance! vertex distance)
(set-car! (cdddr vertex) distance))
;; Define Mutable Queue Data Structure.
(define queue list)
(define-syntax push!
(syntax-rules ()
((_ x Q)
(set! Q (append Q (list x))))))
(define-syntax pop!
(syntax-rules ()
((_ Q)
(let ((x (car Q)))
(set! Q (cdr Q))
x))))
;; Return mutated graph G as a breadth-first tree.
;;
;; Near literal transcription of iterative BFS algorithm
;; from Cormen, et. al., p. 595.
;;
;; Assuming that first element of adjacency list is root
;; to avoid importing or implementing list processing libraries.
(define (bfs G)
(for-each (lambda (u) ;
(set-color! u white) ; `bfs` could be simplified
(set-distance! u infinity) ; by moving graph intialization
(set-predecessor! u nil)) ; outside of procedure
(cdr G)) ;
(let ((s (car G))) ;
(set-color! s gray) ;
(set-distance! s 0) ;
(set-predecessor! s nil) ;
(let ((Q nil))
(push! s Q)
(let loop () ; iteration with "named let" is just tail recursion
(if (not (null? Q))
(begin
(let ((u (pop! Q)))
(for-each (lambda (v)
(if (eq? (color-of v) white)
(begin
(set-color! v gray)
(set-distance! v (+ 1 (distance-of u)))
(set-predecessor! v (vertex-of u))
(push! v Q))))
(neighbors-of u G))
(set-color! u black))
(loop))
G)))))
;; Interpreter Input/Output:
;;
;; > (bfs graph1)
;; ((a black () 0 (b c d))
;; (b black a 1 (a d))
;; (c black a 1 (a))
;; (d black a 1 (a b)))
;; Interpreter Input/Output:
;;
;; > (bfs graph2)
;; ((s black () 0 (r w))
;; (r black s 1 (s v))
;; (t black w 2 (u w x))
;; (u black t 3 (t x y))
;; (v black r 2 (r))
;; (w black s 1 (s t x))
;; (x black w 2 (t u w y))
;; (y black x 3 (u x)))
;; Now redefine `adjlist->graph` to include initialization ...
;; Return an initialized graph data structure given an adjacency list
;; by adding color, predecessor, and distance attributes
;; and setting them to initial values.
;;
;; Assuming that first element of adjacency list is root
;; to avoid importing or implementing list processing libraries.
(define (adjlist->graph adjacency-list)
(let ((root (car adjacency-list))
(rest (cdr adjacency-list)))
(cons (list (car root) gray nil 0 (cadr root))
(map (lambda (vertex)
(list (car vertex) white nil infinity (cadr vertex)))
rest))))
;; ... then reinitialize graphs ...
;; Create initialized graph representation of the first adjacency list.
(define graph1 (adjlist->graph adjlist1))
;; Interpreter Input/Output:
;;
;; > graph1
;; ((a gray () 0 (b c d))
;; (b white () infinity (a d))
;; (c white () infinity (a))
;; (d white () infinity (a b)))
;; Create initialized graph representation of the second adjacency list.
(define graph2 (adjlist->graph adjlist2))
;; Interpreter Input/Output:
;;
;; > graph2
;; ((s gray () 0 (r w))
;; (r white () infinity (s v))
;; (t white () infinity (u w x))
;; (u white () infinity (t x y))
;; (v white () infinity (r))
;; (w white () infinity (s t x))
;; (x white () infinity (t u w y))
;; (y white () infinity (u x)))
;; ... and finally simplify `bfs`.
;; Return mutated graph G as a breadth-first tree.
;;
;; Simplified transcription of iterative BFS algorithm
;; from Cormen, et. al., p. 595.
;;
;; Assuming that first element of adjacency list is root
;; to avoid importing or implementing list processing libraries.
(define (bfs G)
(let ((s (car G)))
(let ((Q nil))
(push! s Q)
(let loop () ; iteration with "named let" is just tail recursion
(if (not (null? Q))
(begin
(let ((u (pop! Q)))
(for-each (lambda (v)
(if (eq? (color-of v) white)
(begin
(set-color! v gray)
(set-distance! v (+ 1 (distance-of u)))
(set-predecessor! v (vertex-of u))
(push! v Q))))
(neighbors-of u G))
(set-color! u black))
(loop))
G)))))
;; Interpreter Input/Output:
;;
;; > (bfs graph1)
;; ((a black () 0 (b c d))
;; (b black a 1 (a d))
;; (c black a 1 (a))
;; (d black a 1 (a b)))
;; Interpreter Input/Output:
;;
;; > (bfs graph2)
;; ((s black () 0 (r w))
;; (r black s 1 (s v))
;; (t black w 2 (u w x))
;; (u black t 3 (t x y))
;; (v black r 2 (r))
;; (w black s 1 (s t x))
;; (x black w 2 (t u w y))
;; (y black x 3 (u x)))
;; Now make recursion explicit, still using mutable data.
;; Return mutated graph G as a breadth-first tree.
;;
;; Explicitly recursive algorithm.
;;
;; Requires passing initialized queue along with arguments
;; (at no cost due to tail recursion).
(define (bfs G Q)
(if (not (null? Q))
(let ((u (pop! Q)))
(for-each (lambda (v)
(if (eq? (color-of v) white)
(begin
(set-color! v gray)
(set-distance! v (+ 1 (distance-of u)))
(set-predecessor! v (vertex-of u))
(push! v Q))))
(neighbors-of u G))
(set-color! u black)
(bfs G Q))
G))
;; Interpreter Input/Output:
;;
;; > (define graph1 (adjlist->graph adjlist1))
;; > (bfs graph1 (queue (car graph1)))
;; ((a black () 0 (b c d))
;; (b black a 1 (a d))
;; (c black a 1 (a))
;; (d black a 1 (a b)))
;; Interpreter Input/Output:
;;
;; > (define graph2 (adjlist->graph adjlist2))
;; > (bfs graph2 (queue (car graph2)))
;; ((s black () 0 (r w))
;; (r black s 1 (s v))
;; (t black w 2 (u w x))
;; (u black t 3 (t x y))
;; (v black r 2 (r))
;; (w black s 1 (s t x))
;; (x black w 2 (t u w y))
;; (y black x 3 (u x)))
;; This demonstrates that breadth-first search can be expressed as a recursive algorithm.
;;
;; Indeed, recursion is the only control structure in Scheme: In reducing
;; the language to its minimum, its developers realized that iteration and recursion
;; are theoretically equivalent. This is also the perspective of lambda calculus.
;;
;; Tail call optimization is necessary to make it work in practice, however.
;;
;; Another discussion of recursive breadth-first search can be found on Stack Overflow:
;; https://stackoverflow.com/questions/2549541/performing-breadth-first-search-recursively?rq=1 .
;;
;; The recursive algorithm could most likely be implemented effectively in Python and other languages.
;; Becuase the recursive implementation was derived by refactoring the "iterative" version,
;; it is expected to have identical running time. This could be verified by adding a counter
;; for iterations and recursive calls.
;;
;; The implementation of the graph as an association list is highly inefficient, however
;; it could easily be replaced with a hash table.
;;
;; Compiling the source to C with the Gambit C compiler would be expected to produce
;; a highly efficient program.
;; A purely functional implementation would require the use of an immutable data structure.
;; This is possible but not necessarily easy to implement.
;;
;; For reference, see "Purely Functional Data Structures" by Chris Okasaki.
;; This program was developed using the Gambit Scheme interpreter: http://gambitscheme.org .
;;
;; To build Gambit, first obtain the source:
;;
;; $ git clone https://github.com/gambit/gambit.git
;;
;; Then checkout the lates release and follow the instructions in INSTALL.txt:
;;
;; $ git checkout v4.8.8
;; $ ./configure
;; $ make current-gsc-boot
;; $ ./configure --enable-single-host
;; $ make -j4 from-scratch
;; $ make check
;;
;; Install Gambit with `sudo make install` or create a Debian package with Checkinstall
;; [https://wiki.debian.org/CheckInstall] :
;;
;; $ sudo checkinstall
;;
;; For interactive debugging in GNU Emacs, add the following lines to .emacs:
;;
;; (add-to-list 'load-path "/usr/local/Gambit/share/emacs/site-lisp/")
;; (require 'gambit)
;; (setq scheme-program-name "/usr/local/Gambit/bin/gsi -:s,d-")
;;
;; Run with `M-x run-scheme` .
;;
;; The manual can be found in /usr/local/Gambit/doc/gambit.pdf .
;; The specification of the relevant version of Scheme can be found here:
;; http://www.schemers.org/Documents/Standards/R5RS/r5rs.pdf