-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmemo.scm
74 lines (69 loc) · 2.53 KB
/
memo.scm
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
(define-syntax cons*
(syntax-rules ()
((_ head rest)
(cons head rest))
((_ head next rest* ...)
(cons head (cons* next rest* ...)))))
;; memoize a single-argument function
;;
;; TODO: something more efficient than an alist?
(: memoize-eq (forall (a b) ((a -> b) -> (a -> b))))
(define memoize-eq
(let ((bad (list 'in-progress)))
(lambda (proc)
(let ((results (make-hash-table test: eq? hash: eq?-hash)))
(lambda (arg)
(let ((res (hash-table-ref
results arg
(lambda ()
(hash-table-set! results arg bad)
(let ((out (proc arg)))
(hash-table-set! results arg out)
out)))))
(if (eq? res bad)
(error "cannot perform recursive memoization")
res)))))))
;; memoize-one-eq is like memoize-eq, but
;; it only caches the latest result
(: memoize-one-eq (forall (a b) ((a -> b) -> (a -> b))))
(define (memoize-one-eq proc)
(let ((last-in (list 'no))
(last-out #f))
(lambda (arg)
(if (eq? arg last-in)
last-out
(let ((res (proc arg)))
(set! last-in arg)
(set! last-out res)
res)))))
;; memoize-lambda takes an expression of the form
;; (memoize-lambda (arg0 arg1 ...) body ...)
;; and yields a lambda that only evaluates its body
;; once for each unique set of arguments (note that the
;; arguments are compared using 'eq?')
;;
;; the strategy here is to break up the lambda
;; into a curried functions for each argument from left to right,
;; so each argument gets applied to a function that returns the
;; memoized continuation of the function for that argument
(define-syntax memoize-lambda
(syntax-rules ()
((_ (formals* ...) body* ...)
(let ((self (memoize-lambda "memoize-args" (formals* ...) body* ...)))
(lambda (formals* ...)
(memoize-lambda "real-body" self (formals* ...)))))
((_ "memoize-args" () body* ...)
(begin body* ...))
((_ "memoize-args" (formal formals* ...) body* ...)
(memoize-eq
(lambda (formal)
(memoize-lambda "memoize-args" (formals* ...) body* ...))))
((_ "real-body" self ())
self)
((_ "real-body" self (formal formals* ...))
(memoize-lambda "real-body" (self formal) (formals* ...)))))
;; sugar for defining memoized functions
(define-syntax define-memoized
(syntax-rules ()
((_ (name formals* ...) body* ...)
(define name (memoize-lambda (formals* ...) body* ...)))))