Advertisement
timothy235

sicp-3-3-1-mutable-list-structure

Feb 24th, 2017
195
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Racket 9.40 KB | None | 0 0
  1. #lang racket
  2. (require racket/mpair)
  3.  
  4. ;; Racket lists are immutable.  To get mutable lists, we'll need the mpair package.
  5.  
  6. ;;;;;;;;;;
  7. ;; 3.12 ;;
  8. ;;;;;;;;;;
  9.  
  10. (define (my-mutable-append x y)
  11.   (if (null? x)
  12.     y
  13.     (mcons (mcar x) (my-mutable-append (mcdr x) y))))
  14.  
  15. (my-mutable-append (mlist 1 2 3) (mlist 4 5 6))
  16. ;; (mcons 1 (mcons 2 (mcons 3 (mcons 4 (mcons 5 (mcons 6 '()))))))
  17.  
  18. (define (my-last-mpair x) ; x cannot be null
  19.   (if (null? (mcdr x))
  20.     x
  21.     (my-last-mpair (mcdr x))))
  22.  
  23. (my-last-mpair (mlist 1 2 3))
  24. ;; (mcons 3 '())
  25.  
  26. (define (my-append! x y)
  27.   (set-mcdr! (my-last-mpair x) y)
  28.   x)
  29.  
  30. (my-append! (mlist 1 2 3) (mlist 4 5 6))
  31. ;; (mcons 1 (mcons 2 (mcons 3 (mcons 4 (mcons 5 (mcons 6 '()))))))
  32.  
  33. (define x (mlist 'a 'b))
  34. (define y (mlist 'c 'd))
  35. (define z (my-mutable-append x y))
  36.  
  37. z
  38. ;; (mcons 'a (mcons 'b (mcons 'c (mcons 'd '()))))
  39.  
  40. (mcdr x)
  41. ;; (mcons 'b '())
  42.  
  43. (define w (my-append! x y))
  44.  
  45. w
  46. ;; (mcons 'a (mcons 'b (mcons 'c (mcons 'd '()))))
  47.  
  48. (mcdr x) ; my-append! has mutated x
  49. ;; (mcons 'b (mcons 'c (mcons 'd '())))
  50.  
  51. ;;;;;;;;;;
  52. ;; 3.13 ;;
  53. ;;;;;;;;;;
  54.  
  55. (define (make-cycle x)
  56.   (set-mcdr! (my-last-mpair x) x)
  57.   x)
  58.  
  59. (define z2 (make-cycle (mlist 'a 'b 'c)))
  60. z2
  61. ;; #0=(mcons 'a (mcons 'b (mcons 'c #0#))) ; #0# references the 'graph tag' #0
  62.  
  63. #|
  64. This expression:
  65.  
  66. (my-last-mpair z2)
  67.  
  68. hangs the repl.
  69. |#
  70.  
  71. ;;;;;;;;;;
  72. ;; 3.14 ;;
  73. ;;;;;;;;;;
  74.  
  75. (define (mystery x)
  76.   (define (loop x y)
  77.     (if (null? x)
  78.       y
  79.       (let ([temp (mcdr x)])
  80.         (set-mcdr! x y)
  81.         (loop temp x))))
  82.   (loop x '()))
  83.  
  84. (define v (mlist 'a 'b 'c 'd))
  85. (define w2 (mystery v))
  86.  
  87. v ; mystery has mutated v
  88. ;; (mcons 'a '())
  89.  
  90. w2 ; mystery reverses a list
  91. ;; (mcons 'd (mcons 'c (mcons 'b (mcons 'a '()))))
  92.  
  93. ;;;;;;;;;;
  94. ;; 3.15 ;;
  95. ;;;;;;;;;;
  96.  
  97. (define (set-to-wow! x)
  98.   (set-mcar! (mcar x) 'wow)
  99.   x)
  100.  
  101. (define x2 (mlist 'a 'b))
  102. (define z3 (mcons x2 x2))
  103. (define z4 (mcons (mlist 'a 'b) (mlist 'a 'b)))
  104.  
  105. x2
  106. ;; (mcons 'a (mcons 'b '()))
  107. z3
  108. ;; (mcons (mcons 'a (mcons 'b '())) (mcons 'a (mcons 'b '())))
  109. z4
  110. ;; (mcons (mcons 'a (mcons 'b '())) (mcons 'a (mcons 'b '())))
  111.  
  112. (set-to-wow! z3)
  113. ;; (mcons (mcons 'wow (mcons 'b '())) (mcons 'wow (mcons 'b '())))
  114. (set-to-wow! z4)
  115. ;; (mcons (mcons 'wow (mcons 'b '())) (mcons 'a (mcons 'b '())))
  116.  
  117. ;;;;;;;;;;
  118. ;; 3.16 ;;
  119. ;;;;;;;;;;
  120.  
  121. ;; Ben's code is wrong because it assumes that all pointers in x will be distinct,
  122. ;; and that is not always the case.
  123.  
  124. (define (count-mpairs x)
  125.   (if (not (mpair? x))
  126.     0
  127.     (+ (count-mpairs (mcar x))
  128.        (count-mpairs (mcdr x))
  129.        1)))
  130.  
  131. (count-mpairs (mcons 'a (mcons 'b (mcons 'c '()))))
  132. ;; 3
  133.  
  134. (define y2 (mcons 'a '()))
  135. (count-mpairs (mcons (mcons y2 y2) '()))
  136. ;; 4
  137.  
  138. (define z5 (mcons y2 y2))
  139. (count-mpairs (mcons z5 z5))
  140. ;; 7
  141.  
  142. ;; And we could hang the repl by creating a cyclical list like above.
  143.  
  144. ;;;;;;;;;;
  145. ;; 3.17 ;;
  146. ;;;;;;;;;;
  147.  
  148. (define (new-count-mpairs x)
  149.   (let ([examined-mpairs '()])
  150.     (define (examined? mp)
  151.       (mmember mp examined-mpairs))
  152.     (define (set-examined! mp)
  153.       (set! examined-mpairs (mcons mp examined-mpairs)))
  154.     (define (examine y)
  155.       (cond
  156.         [(not (mpair? y)) 0]
  157.         [(examined? y) (+ (examine (mcar y)) (examine (mcdr y)))]
  158.         [else (set-examined! y)
  159.               (+ 1 (examine (mcar y)) (examine (mcdr y)))]))
  160.     (examine x)))
  161.  
  162. (new-count-mpairs (mlist 'a 'b 'c))
  163. ;; 3
  164.  
  165. (new-count-mpairs (mcons (mcons y2 y2) '()))
  166. ;; 3
  167.  
  168. (new-count-mpairs (mcons z5 z5))
  169. ;; 3
  170.  
  171. ;;;;;;;;;;
  172. ;; 3.18 ;;
  173. ;;;;;;;;;;
  174.  
  175. #|
  176. Here's the idea:
  177.  
  178. Lists form rooted directed graphs, where cons pairs are interior nodes,
  179. non-pair elements are leaves, and the mcar and mcdr pointers are the edges.
  180. The root is the list itself ie the outer-most cons pair.
  181.  
  182. Since the digraph is rooted, we can define the level of a node as the minimum
  183. distance to the root.
  184.  
  185. For example, if x is the list (mcons 'a (mcons 'b (mcons 'c '()))), then x is
  186. the root at level 0, 'a and (mlist 'b 'c) are at level 1, 'b and (mlist 'c) are
  187. at level 2, and 'c and '() are at level 3.
  188.  
  189. To detect a cycle, we'll look for a node with a child node on a higher level,
  190. ie a pair whose mcar or mcdr is on a higher level.
  191. |#
  192.  
  193. (define (contains-cycle? x)
  194.   (define level-table (make-hash))
  195.   (define (has-level? node) (member node (hash-keys level-table)))
  196.   (define (assign-level node level) (hash-set! level-table node level))
  197.   (define (level node) (hash-ref level-table node))
  198.   (define (examine node current-level)
  199.     ; Return true if any descendant of node is on a higher level.
  200.     (cond [(not (mpair? node)) #f]
  201.           [(has-level? node)
  202.            (or (< (level node) current-level)
  203.                (examine (mcar node) (add1 current-level))
  204.                (examine (mcdr node) (add1 current-level)))]
  205.           [else
  206.             (assign-level node current-level)
  207.             (or (examine (mcar node) (add1 current-level))
  208.                 (examine (mcdr node) (add1 current-level)))]))
  209.   (examine x 0))
  210.  
  211. (contains-cycle? (mlist 'a 'b 'c))
  212. ;; #f
  213.  
  214. (define y3 (mlist 'a))
  215. (contains-cycle? (mcons y3 y3))
  216. ;; #f
  217.  
  218. (contains-cycle? (make-cycle (mlist 'a 'b 'c)))
  219. ;; #t
  220.  
  221. ;;;;;;;;;;
  222. ;; 3.19 ;;
  223. ;;;;;;;;;;
  224.  
  225. #|
  226. Detect cycles in lists using a constant space algorithm.
  227.  
  228. I never would have thought of this.  The answer is Floyd's cycle detection
  229. algorithm, also called the tortoise and the hare.  The algorithm is constant
  230. space because it only tracks two pointers, the tortoise and the hare.
  231.  
  232. The code below is from The Scheme Programming Language by Dybvig.
  233. |#
  234.  
  235. (define (race hare tortoise)
  236.   ;; Go down hare and tortoise by taking cdr's but go twice as fast down hare.
  237.   ;; Return true if you never see equal pairs ie the hare wins the race.
  238.   (cond [(mpair? hare)
  239.          (define faster-hare (mcdr hare))
  240.          (if (mpair? faster-hare)
  241.            (and (not (eq? faster-hare tortoise))
  242.                 (race (mcdr faster-hare) (mcdr tortoise)))
  243.            (null? faster-hare))]
  244.         [else (null? hare)]))
  245.  
  246. (define (floyd-cycle? x)
  247.   (not (race x x)))
  248.  
  249. (floyd-cycle? (mlist 'a 'b 'c))
  250. ;; #f
  251.  
  252. ;; Recall that y3 is (mlist 'a).
  253. (floyd-cycle? (mcons y3 y3))
  254. ;; #f
  255.  
  256. (floyd-cycle? (make-cycle (mlist 'a 'b 'c)))
  257. ;; #t
  258.  
  259. ;;;;;;;;;;
  260. ;; 3.20 ;;
  261. ;;;;;;;;;;
  262.  
  263. (define (my-cons x y)
  264.   (define (set-x! v) (set! x v))
  265.   (define (set-y! v) (set! y v))
  266.   (define (dispatch m)
  267.     (cond [(eq? m 'car) x]
  268.           [(eq? m 'cdr) y]
  269.           [(eq? m 'set-car!) set-x!]
  270.           [(eq? m 'set-cdr!) set-y!]
  271.           [else (error "Undefined operation -- my-cons:" m)]))
  272.   dispatch)
  273. (define (my-car z) (z 'car))
  274. (define (my-cdr z) (z 'cdr))
  275. (define (set-car! z new-value)
  276.   ((z 'set-car!) new-value)
  277.   z)
  278. (define (set-cdr! z new-value)
  279.   ((z 'set-cdr!) new-value)
  280.   z)
  281.  
  282. ;; Describe the environment structures that result from evaluating these
  283. ;; expressions:
  284.  
  285. (define x3 (my-cons 1 2))
  286. (define z6 (my-cons x3 x3))
  287. (set-car! (my-cdr z6) 17)
  288. ;; #<procedure:dispatch>
  289. (my-car x3)
  290. ;; 17
  291.  
  292. #|
  293. We're using x3 and z6 for what the book calls x and z to avoid duplicate
  294. definitions from earlier exercises.
  295.  
  296. (define x3 (my-cons 1 2)) creates a variable x3 in the global environment and
  297. binds it to the result of calling my-cons on 1 and 2.  Calling my-cons on 1 and
  298. 2 creates a new binding frame E1 below the global environment where the formal
  299. parameters of my-cons, namely x and y, are bound to the passed values 1 and 2,
  300. and then the body of my-cons is evaluated in E1.  Evaluating the body of
  301. my-cons in E1 creates new functions set-x!, set-y!, and dispatch, whose
  302. enclosing environments are E1.  x3 is then bound to the dispatch function in
  303. E1.
  304.  
  305. (define z6 (my-cons x3 x3)) creates a variable z6 in the global environment and
  306. binds it to the result of calling my-cons on x3 and x3.  Calling my-cons on x3
  307. and x3 creates a new binding frame E2 below the global environment where the
  308. formal parameters of my-cons, namely x and y, are bound to the passed values x3
  309. and x3, and then the body of my-cons is evaluated in E2.  Evaluating the body
  310. of my-cons in E2 creates new functions set-x!, set-y!, and dispatch, whose
  311. enclosing environments are E2.  z6 is then bound to the dispatch function in
  312. E2.
  313.  
  314. (set-car! (my-cdr z6) 17) creates a new binding frame E3 below the global
  315. environment where the formal parameters of set-car! are bound to the passed
  316. values.  Namely, z is bound to the result of (my-cdr z6), and new-value is
  317. bound to 17.
  318.  
  319. (my-cdr z6) creates a new binding frame E4 below the global environment where
  320. the formal parameter of my-cdr, z, is bound to the passed value z6, which is a
  321. pointer to the dispatch function in E2.  Evaluating the body of my-cdr in E4
  322. calls the dispatch function in E2 and applies it to the symbol 'cdr.  Applying
  323. the dispatch function in E2 to 'cdr returns the value of the formal parameter y
  324. in E2 which was bound to x3.  So my-cdr of z6 is x3, which points to the
  325. dispatch function in E1.
  326.  
  327. So now we evaluate the body of set-car! in E3 where the formal parameters z and
  328. new-value have been bound to x3 and 17.  To do that we first have to evaluate
  329. (z 'set-car!) in E3, but this is just x3, the dispatch function in E1, applied
  330. to 'set-car!, which is the set-x! function in E1.  So now we take the set-x!
  331. function in E1 and apply it to new-value in E3, which is 17.  This changes the
  332. value of the formal parameter x in E1 from 1 to 17, ie we have changed the car
  333. of x3 from 1 to 17, which is what we do observe.
  334. |#
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement