Advertisement
timothy235

sicp-3-3-3-representing-tables

Feb 28th, 2017
171
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Racket 9.98 KB | None | 0 0
  1. #lang racket
  2. (require racket/mpair)
  3.  
  4. ;;;;;;;;;;
  5. ;; 3.24 ;;
  6. ;;;;;;;;;;
  7.  
  8. (define (make-key-test-table same-key?)
  9.   ;; Create a procedural table that tests equality of keys using same-key?.
  10.   (define local-table (mlist '*table*))
  11.   (define (my-assoc key records)
  12.     (cond [(empty? records) false]
  13.           [else
  14.             (define record (mcar records))
  15.             (if (same-key? key (mcar record))
  16.               record
  17.               (my-assoc key (mcdr records)))]))
  18.   (define (lookup key)
  19.     (define record (my-assoc key (mcdr local-table)))
  20.     (if record
  21.       (mcdr record)
  22.       false))
  23.   (define (insert! key value)
  24.     (define record (my-assoc key (mcdr local-table)))
  25.     (if record
  26.       (set-mcdr! record value)
  27.       (set-mcdr! local-table
  28.                  (mcons (mcons key value)
  29.                         (mcdr local-table))))
  30.     'ok)
  31.   (define (dispatch m)
  32.     (cond [(eq? m 'lookup-proc) lookup]
  33.           [(eq? m 'insert-proc!) insert!]
  34.           [else (error "Unknown operation -- TABLE" m)]))
  35.   dispatch)
  36.  
  37. ;; tests
  38.  
  39. (define (same-num? x y) (< (abs (- x y)) 0.1))
  40. (define t1 (make-key-test-table same-num?))
  41. ((t1 'insert-proc!) 1.032 'abc)
  42. ;; 'ok
  43. ((t1 'lookup-proc) 0.998)
  44. ;; 'abc
  45.  
  46. ;;;;;;;;;;
  47. ;; 3.25 ;;
  48. ;;;;;;;;;;
  49.  
  50. (define (make-flexible-table)
  51.   ;; Create a procedural table of flexible dimension.
  52.   (define local-table (mlist '*table*))
  53.   (define (my-assoc key records)
  54.     (cond [(empty? records) false]
  55.           [else
  56.             (define record (mcar records))
  57.             (if (equal? key (mcar record))
  58.               record
  59.               (my-assoc key (mcdr records)))]))
  60.   (define (lookup key . rest-of-keys)
  61.     (define (lookup-loop k ks tbl)
  62.       (cond [(empty? ks) ; search for record in current table
  63.              (define record (my-assoc k (mcdr tbl)))
  64.              (if record
  65.                (mcdr record)
  66.               false)]
  67.             [else ; recurse through next table or false if none
  68.               (define next-tbl (my-assoc k (mcdr tbl)))
  69.               (if next-tbl
  70.                 (lookup-loop (first ks) (rest ks) next-tbl) ; ks is a regular list
  71.                 false)]))
  72.     (lookup-loop key rest-of-keys local-table))
  73.   (define (insert! key . args)
  74.     (when (zero? (length args)) (error "No value given -- INSERT!"))
  75.     (define value (last args))
  76.     (define rest-of-keys (drop-right args 1))
  77.     (define (insert-loop! k ks tbl)
  78.       (cond [(empty? ks) ; insert in current table
  79.              (define record (my-assoc k (mcdr tbl)))
  80.              (if record
  81.                (set-mcdr! record value)
  82.                (set-mcdr! tbl
  83.                           (mcons (mcons k value)
  84.                                  (mcdr tbl))))]
  85.             [else ; recurse, creating next table if necessary
  86.               (define next-tbl (my-assoc k (mcdr tbl)))
  87.               (cond [next-tbl
  88.                       (insert-loop! (first ks) (rest ks) next-tbl)]
  89.                     [else
  90.                       (set-mcdr! tbl
  91.                                  (mcons (mlist k)
  92.                                         (mcdr tbl)))
  93.                       (insert-loop! (first ks)
  94.                                     (rest ks)
  95.                                     (mcar (mcdr tbl)))])]))
  96.     (insert-loop! key rest-of-keys local-table)
  97.     'ok)
  98.   (define (dispatch m)
  99.     (cond [(eq? m 'lookup-proc) lookup]
  100.           [(eq? m 'insert-proc!) insert!]
  101.           [else (error "Unknown operation -- TABLE:" m)]))
  102.   dispatch)
  103.  
  104. ;; tests
  105.  
  106. (define t2 (make-flexible-table))
  107. ((t2 'insert-proc!) 1 'a)
  108. ;; 'ok
  109. ((t2 'insert-proc!) 2 3 'bc)
  110. ;; 'ok
  111. ((t2 'insert-proc!) 4 5 6 7 'defg)
  112. ;; 'ok
  113. ((t2 'lookup-proc) 1)
  114. ;; 'a
  115. ((t2 'lookup-proc) 2 3)
  116. ;; 'bc
  117. ((t2 'lookup-proc) 4 5 6 7)
  118. ;; 'defg
  119.  
  120. ;;;;;;;;;;
  121. ;; 3.26 ;;
  122. ;;;;;;;;;;
  123.  
  124. ;; Implement one-dimensional tables.  Use the binary search tree representation of
  125. ;; sets from section 2.3.3 to organize the list of key-value pairs.
  126.  
  127. ;; Assume keys are integers.
  128.  
  129. ;; mutable binary trees
  130.  
  131. (define mfirst mcar)
  132. (define (msecond mutable-list) (mcar (mcdr mutable-list)))
  133. (define (mthird mutable-list) (mcar (mcdr (mcdr mutable-list))))
  134.  
  135. (define (entry tree) (mfirst tree))
  136. (define (left-branch tree) (msecond tree))
  137. (define (right-branch tree) (mthird tree))
  138. (define (make-tree entry left right) (mlist entry left right))
  139.  
  140. (define (tree-lookup key st) ; searches in log time if tree balanced
  141.   (cond [(empty? st) false]
  142.         [(= key (mcar (entry st))) (entry st)]
  143.         [(< key (mcar (entry st)))
  144.          (tree-lookup key (left-branch st))]
  145.         [else (tree-lookup key (right-branch st))]))
  146.  
  147. (define (tree-adjoin-set record st)
  148.   (cond [(empty? st) (make-tree record empty empty)]
  149.         [(= (mcar record) (mcar (entry st))) st]
  150.         [(< (mcar record) (mcar (entry st)))
  151.          (make-tree (entry st)
  152.                     (tree-adjoin-set record (left-branch st))
  153.                     (right-branch st))]
  154.         [else (make-tree (entry st)
  155.                          (left-branch st)
  156.                          (tree-adjoin-set record (right-branch st)))]))
  157.  
  158. ;; bst table
  159.  
  160. (define (make-bst-table)
  161.   ;; Create a procedural table that uses a bst to store records.
  162.   (define local-table (mlist '*table*))
  163.   (define (lookup key)
  164.     (define record (tree-lookup key (mcdr local-table)))
  165.     (if record
  166.       (msecond record)
  167.       false))
  168.   (define (insert! key value)
  169.     (define record (tree-lookup key (mcdr local-table)))
  170.     (if record
  171.       (set-mcdr! record value)
  172.       (set-mcdr! local-table
  173.                  (tree-adjoin-set (mlist key value)
  174.                                   (mcdr local-table))))
  175.     'ok)
  176.   (define (dispatch m)
  177.     (cond [(eq? m 'lookup-bst-proc) lookup]
  178.           [(eq? m 'insert-bst-proc!) insert!]
  179.           [else (error "Unknown message -- DISPATCH" m)]))
  180.   dispatch)
  181.  
  182. ;; test
  183.  
  184. (define t3 (make-bst-table))
  185. ((t3 'insert-bst-proc!) 1 'a)
  186. ;; 'ok
  187. ((t3 'insert-bst-proc!) 2 'b)
  188. ;; 'ok
  189. ((t3 'insert-bst-proc!) 3 'c)
  190. ;; 'ok
  191. ((t3 'lookup-bst-proc) 1)
  192. ;; 'a
  193. ((t3 'lookup-bst-proc) 2)
  194. ;; 'b
  195. ((t3 'lookup-bst-proc) 3)
  196. ;; 'c
  197.  
  198. ;; I compared the bst table lookup times to regular unordered-list table lookup
  199. ;; times using the same set of 100,000 randomized key-value pairs.  About half the
  200. ;; uol-table lookup times were 15 ms while all the bst-table lookup times were 0
  201. ;; ms.  So I think we are getting log lookup times for the bst table when the keys
  202. ;; have been inserted randomly as expected.
  203.  
  204. ;;;;;;;;;;
  205. ;; 3.27 ;;
  206. ;;;;;;;;;;
  207.  
  208. (define (fib n)
  209.   (cond [(zero? n) 0]
  210.         [(= n 1) 1]
  211.         [else (+ (fib (sub1 n))
  212.                  (fib (- n 2)))]))
  213.  
  214. (define (memoize f)
  215.   (define table (make-bst-table))
  216.   (lambda (x)
  217.     (define previously-computed-result ((table 'lookup-bst-proc) x))
  218.     (cond [previously-computed-result
  219.             previously-computed-result]
  220.           [else
  221.             (define result (f x))
  222.             ((table 'insert-bst-proc!) x result)
  223.             result])))
  224.  
  225. (define memo-fib
  226.   (memoize (lambda (n)
  227.              (cond [(zero? n) 0]
  228.                    [(= n 1) 1]
  229.                    [else (+ (memo-fib (sub1 n))
  230.                             (memo-fib (- n 2)))]))))
  231.  
  232. ;; test
  233.  
  234. (for ([n (in-range 25 45 5)])
  235.   (printf "n = ~a ~n" n)
  236.   (time (displayln (fib n)))
  237.   (time (displayln (memo-fib n))))
  238. ;; n = 25
  239. ;; 75025
  240. ;; cpu time: 31 real time: 22 gc time: 0
  241. ;; 75025
  242. ;; cpu time: 0 real time: 0 gc time: 0
  243. ;; n = 30
  244. ;; 832040
  245. ;; cpu time: 125 real time: 132 gc time: 0
  246. ;; 832040
  247. ;; cpu time: 0 real time: 0 gc time: 0
  248. ;; n = 35
  249. ;; 9227465
  250. ;; cpu time: 1437 real time: 1434 gc time: 0
  251. ;; 9227465
  252. ;; cpu time: 0 real time: 0 gc time: 0
  253. ;; n = 40
  254. ;; 102334155
  255. ;; cpu time: 15594 real time: 16477 gc time: 32
  256. ;; 102334155
  257. ;; cpu time: 0 real time: 0 gc time: 0
  258.  
  259. ;; Describe the environment structure created by (memo-fib 3).
  260.  
  261. ;; The lambda of n in the definition of memo-fib will be called the memo-fib
  262. ;; lambda.  The lambda of x in the definition of memoize will be called the
  263. ;; memoize lambda.
  264.  
  265. ;; memo-fib and memoize are variable names in the global environment.  memo-fib is
  266. ;; bound to the result of calling memoize on the memo-fib lambda.  Calling memoize
  267. ;; on the memo-fib lambda creates a binding frame E1 below the global environment
  268. ;; where the formal parameter f of memoize is bound to the memo-fib lambda, the
  269. ;; table is created, and the body of memoize is evaluated.  Evaluating the body of
  270. ;; memoize in E1 returns a function object whose defining environment is E1.
  271. ;; memo-fib is bound to this function.  This memo-fib function is not the same as
  272. ;; the memo-fib lambda.  For example, the memo-fib function has formal parameter
  273. ;; x, while the memo-fib lambda has formal parameter n.
  274.  
  275. ;; It might seem weird that memo-fib, a variable bound in the global environment,
  276. ;; is bound to a function whose defining environment is not the global
  277. ;; environment, but that's what happens when you have local data like table.
  278.  
  279. ;; Calling the memo-fib function on 3 creates a binding frame E2 below E1 where x
  280. ;; is bound to 3 and the body of memo-fib is evaluated.  Evaluating the body of
  281. ;; memo-fib looks up 3 in the table, does not find it, and so computes (f 3).
  282.  
  283. ;; f is bound to the memo-fib lambda.  So this call creates another binding frame
  284. ;; E3 below E1 where n is bound to 3, and the body of the memo-fib lambda is
  285. ;; evaluated.  This is where we get the recursive calls to memo-fib(2) and
  286. ;; memo-fib(1), and the whole process repeats itself.
  287.  
  288. ;; Explain why memo-fib runs in linear time.
  289.  
  290. ;; memo-fib creates a tree of recursive calls n deep.  Once the function reaches
  291. ;; the leaves of the tree and begins to return, it doesn't need to keep returning
  292. ;; to the leaves to compute each intermediate fib(k), it just looks up the values
  293. ;; of fib(k - 1) and fib(k - 2) in the table and adds them.  So memo-fib only does
  294. ;; constant work at each level.
  295.  
  296. ;; Note that defining memo-fib as (memoize fib) would not work because the
  297. ;; recursive calls would not be memoized, just the outermost call.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement