Advertisement
timothy235

sicp-4-1-6-internal-definitions

Mar 14th, 2017
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Racket 10.33 KB | None | 0 0
  1. #lang racket
  2.  
  3. (require "3-5-streams.rkt"
  4.          "4-1-4-repl-program.rkt")
  5.  
  6. ;;;;;;;;;;
  7. ;; 4.16 ;;
  8. ;;;;;;;;;;
  9.  
  10. (define (lookup-variable-value var env)
  11.   (define b (find-binding-in-env var env))
  12.   (cond [(not b) (error "Unbound variable" var)]
  13.         [else
  14.           (define val (binding-value b))
  15.           (if (eq? val '*unassigned*)
  16.             (error "Unassigned variable" var)
  17.             val)]))
  18.  
  19. (define (scan-out-defines body)
  20.   (define defines (filter definition? body))
  21.   (cond [(empty? defines) body]
  22.         [else
  23.           (define rest-of-body (filter-not definition? body))
  24.           (define vars (map definition-variable defines))
  25.           (define vals (map definition-value defines))
  26.           (define bindings
  27.             (for/list ([v vars])
  28.                       (list v '*unassigned*)))
  29.           (define assigns
  30.             (for/list ([v vars]
  31.                        [e vals])
  32.                       (list 'set! v e)))
  33.           (make-let
  34.             bindings
  35.             (append assigns rest-of-body))]))
  36.  
  37. ;; test
  38.  
  39. (scan-out-defines '((define a 1)
  40.                     (define b 2)
  41.                     (define (f x) (+ x 5))
  42.                     (set! a 3)
  43.                     (f (* a b))))
  44. ;; '(let ((a *unassigned*) (b *unassigned*) (f *unassigned*))
  45.    ;; (set! a 1)
  46.    ;; (set! b 2)
  47.    ;; (set! f (lambda (x) (+ x 5)))
  48.    ;; (set! a 3)
  49.    ;; (f (* a b)))
  50.  
  51. ;; The test file ran fine with scan-out-defines installed in either make-procedure or
  52. ;; procedure-body.  I would prefer to put it in procedure-body though because
  53. ;; scan-out-defines is a syntactic transformation used to aid evaluation, and not a
  54. ;; syntactic form we are trying to enforce on all procedures.  So I think it belongs
  55. ;; with the selectors rather than the constructors.
  56.  
  57. ;;;;;;;;;;
  58. ;; 4.17 ;;
  59. ;;;;;;;;;;
  60.  
  61. ;; (lambda <vars>
  62.   ;; (define u <e1>)
  63.   ;; (define v <e2>)
  64.   ;; <e3>)
  65.  
  66. ;; (lambda <vars>
  67.   ;; (let ((u '*unassigned*)
  68.         ;; (v '*unassigned*))
  69.     ;; (set! u <e1>)
  70.     ;; (set! v <e2>)
  71.     ;; <e3>))
  72.  
  73. #|
  74. Calling the top lambda creates a binding frame below its enclosing environment
  75. where vars are bound.  Then the body is evaluated in that binding frame, which
  76. adds u and v to the frame, binding them to e1 and e2, respectively, then
  77. evaluating e3.
  78.  
  79. Calling the bottom lambda creates a binding frame where vars are bound, just like
  80. above.  Then the let statement is evaluated in that binding frame.  The let is
  81. treated as a procedure application, so its evaluation creates a new binding frame,
  82. below the one just created, where u and v are bound to '*unassigned*.  Then the
  83. body of the let statement is evaluated in this subframe, setting u and v to e1 and
  84. e2, respectively, and then evaluating e3.
  85.  
  86. The effect is the same as if vars, u and v, were all defined in the same frame.
  87. We have, in effect, taken a frame that is a leaf node in the environment graph and
  88. split it in two, while requiring the body to be evaluated in the new lower frame.
  89. The body will have access to the same set of bindings as before.  The only
  90. difference is that lookup-variable-value may have to recurse one extra time to
  91. find the value of a variable left in the upper frame.
  92.  
  93. One way to get rid of the extra frame is to have the interpreter scan lambdas for
  94. internal defines, add any definition-variables to the lambda parameters, curried
  95. to take the value '*unassigned*, and add the set! statements to the beginning of
  96. the body.
  97. |#
  98.  
  99. ;;;;;;;;;;
  100. ;; 4.18 ;;
  101. ;;;;;;;;;;
  102.  
  103. (define (integral delayed-integrand initial-value dt)
  104.   (cons-stream initial-value
  105.                (let ([integrand (force delayed-integrand)])
  106.                  (if (stream-null? integrand)
  107.                    the-empty-stream
  108.                    (integral (delay (stream-cdr integrand))
  109.                              (+ (* dt (stream-car integrand))
  110.                                 initial-value)
  111.                              dt)))))
  112.  
  113. ;; The define statement involving a let with mutually recursive bindings is rejected
  114. ;; by the Racket reader:
  115.  
  116. ;; (define (solve f y0 dt)
  117.   ;; (let ([y (integral (delay dy) y0 dt)]
  118.         ;; [dy (my-stream-map f y)])
  119.     ;; y))
  120. ;; ;; . dy: unbound identifier in module in: dy
  121.  
  122. ;; In contrast, the '*unassigned* strategy works:
  123.  
  124. (define (solve2 f y0 dt)
  125.   (let ([y '*unassigned*]
  126.         [dy '*unassigned*])
  127.     (set! y (integral (delay dy) y0 dt))
  128.     (set! dy (my-stream-map f y))
  129.     y))
  130.  
  131. (my-stream-ref (solve2 (lambda (y) y) 1 0.001) 1000)
  132. ;; 2.716923932235896
  133.  
  134. ;; The alternative strategy does not work.  The Racket reader accepts the layered
  135. ;; lets that hide the mutual recursion, but evaluating the inner let expressions
  136. ;; produces errors because y and dy are still bound to '*unassigned* when passed to
  137. ;; integral and my-stream-map.
  138.  
  139. (define (solve3 f y0 dt)
  140.   (let ([y '*unassigned*]
  141.         [dy '*unassigned*])
  142.     (let ([a (integral (delay dy) y0 dt)]
  143.           [b (my-stream-map f y)])
  144.       (set! y a)
  145.       (set! dy b)
  146.       y)))
  147.  
  148. ;; (my-stream-ref (solve3 (lambda (y) y) 1 0.001) 1000)
  149. ;; ;; . . car: contract violation
  150.   ;; ;; expected: pair?
  151.   ;; ;; given: '*unassigned*
  152.  
  153. ;; Scheme also provides letrec, recursive let, that implements 'simultaneous
  154. ;; defines'.  Using internal defines is equivalent to letrec.  The Racket style guide
  155. ;; prefers internal defines over any of the let forms.
  156.  
  157. (define (solve4 f y0 dt)
  158.   (letrec ([y (integral (delay dy) y0 dt)]
  159.            [dy (my-stream-map f y)])
  160.     y))
  161.  
  162. (my-stream-ref (solve4 (lambda (y) y) 1 0.001) 1000)
  163. ;; 2.716923932235896
  164.  
  165. ;;;;;;;;;;
  166. ;; 4.19 ;;
  167. ;;;;;;;;;;
  168.  
  169. ;; Racket follows Alyssa's view and produces an 'a undefined' error.
  170.  
  171. ;; (let ([a 1])
  172.   ;; (define (f x)
  173.     ;; (define b (+ a x))
  174.     ;; (define a 5)
  175.     ;; (+ a b))
  176.   ;; (f 10))
  177. ;; ;; . . a: undefined;
  178.  ;; ;; cannot use before initialization
  179.  
  180. ;; However, this only produces an error because of the (define a ...) statement in
  181. ;; the body of f.  Remove that and the a from the environment is used:
  182.  
  183. (let ([a 1])
  184.   (define (f x)
  185.     (define b (+ a x))
  186.     ;; (define a 5)
  187.     (+ a b))
  188.   (f 10))
  189. ;; 12
  190.  
  191. ;; So Racket is implementing something like our scan-out-defines behind the scenes.
  192.  
  193. ;; I don't see an easy way to implement Eva's preferred behavior.  I think
  194. ;; scan-out-defines would have to create a graph of dependencies for the
  195. ;; definition-variables, and then place values in their locations in an order
  196. ;; compatible with their dependencies.
  197.  
  198. ;;;;;;;;;;
  199. ;; 4.20 ;;
  200. ;;;;;;;;;;
  201.  
  202. ;; ;; Add this clause to my-eval:
  203.     ;; [(letrec? expr) (my-eval (letrec->simultaneous-lets expr) env)]
  204.  
  205. (define (letrec? expr) (tagged-list? expr 'letrec))
  206. (define (letrec-bindings expr) (second expr))
  207. (define (letrec-body expr) (drop expr 2))
  208.  
  209. (define (letrec->simultaneous-lets expr)
  210.   (define bindings (letrec-bindings expr))
  211.   (cond [(empty? bindings)
  212.          (letrec-body expr)]
  213.         [else
  214.           (define vars (map first bindings))
  215.           (define vals (map second bindings))
  216.           (define new-bindings
  217.             (for/list ([v vars])
  218.                       (list v '*unassigned*)))
  219.           (define assigns
  220.             (for/list ([v vars]
  221.                        [e vals])
  222.                       (list 'set! v e)))
  223.           (make-let
  224.             new-bindings
  225.             (append assigns (letrec-body expr)))]))
  226.  
  227. (letrec->simultaneous-lets
  228.   '(letrec () (+ 1 2)))
  229. ;; '(+ 1 2)
  230.  
  231. (letrec->simultaneous-lets
  232.   '(letrec ((even? (lambda (n) (if (zero? n) true (odd? (sub1 n)))))
  233.             (odd? (lambda (n) (if (zero? n) false (even? (sub1 n))))))
  234.      (even? 12)))
  235. ;; '(let ((even? *unassigned*) (odd? *unassigned*))
  236.    ;; (set! even? (lambda (n) (if (zero? n) true (odd? (sub1 n)))))
  237.    ;; (set! odd? (lambda (n) (if (zero? n) false (even? (sub1 n)))))
  238.    ;; (even? 12))
  239.  
  240. (letrec->simultaneous-lets
  241.   '(letrec ((even? (lambda (n) (if (zero? n) true (odd? (sub1 n)))))
  242.             (odd? (lambda (n) (if (zero? n) false (even? (sub1 n))))))
  243.      'side-effect
  244.      (even? 12)))
  245. ;; '(let ((even? *unassigned*) (odd? *unassigned*))
  246.    ;; (set! even? (lambda (n) (if (zero? n) true (odd? (sub1 n)))))
  247.    ;; (set! odd? (lambda (n) (if (zero? n) false (even? (sub1 n)))))
  248.    ;; 'side-effect
  249.    ;; (even? 12))
  250.  
  251. (define (f x)
  252.   (letrec ([even? (lambda (n) (if (zero? n) true (odd? (sub1 n))))]
  253.            [odd? (lambda (n) (if (zero? n) false (even? (sub1 n))))])
  254.     'rest-of-body))
  255.  
  256. ;; The environment diagram for the letrec is just what you would expect.  The letrec
  257. ;; is transformed into a let, with even? and odd? initially bound to dummy values,
  258. ;; and then set! to their real lambda values.  set!, which evaluates its argument,
  259. ;; can do this because each variable, even? and odd?, have assigned locations in
  260. ;; memory due to having already been bound to the dummy values.  So neither is a free
  261. ;; variable in the definition of the other, and no error is raised.  In effect, even?
  262. ;; and odd? are bound simultaneously in the one binding frame, with each value
  263. ;; depending on the other.
  264.  
  265. ;; Using let instead of letrec is a non-starter.  The interpreter creates a binding
  266. ;; frame for the let statement where it tries to bind even? and odd? to the values
  267. ;; their definitions reduce to.  But whichever definition it tries to evaluate first,
  268. ;; it will always encounter the other, still undefined, raising an error.
  269.  
  270. ;;;;;;;;;;
  271. ;; 4.21 ;;
  272. ;;;;;;;;;;
  273.  
  274. ((lambda (n)
  275.    ((lambda (fact)
  276.       (fact fact n))
  277.     (lambda (ft k)
  278.       (if (= k 1)
  279.         1
  280.         (* k (ft ft (sub1 k)))))))
  281.  10)
  282. ;; 3628800 ; this is 10!
  283.  
  284. ((lambda (n)
  285.    ((lambda (fib)
  286.       (fib fib n))
  287.     (lambda (ft k)
  288.       (if (< k 2)
  289.         k
  290.         (+ (ft ft (sub1 k))
  291.            (ft ft (- k 2)))))))
  292.  10)
  293. ;; 55 ; this is fib(10)
  294.  
  295. (define (f2 x)
  296.   ((lambda (my-even? my-odd?)
  297.      (my-even? my-even? my-odd? x))
  298.    (lambda (ev? od? n)
  299.      (if (zero? n)
  300.       true
  301.        (od? od? ev? (sub1 n))))
  302.    (lambda (od? ev? n)
  303.      (if (zero? n)
  304.       false
  305.        (ev? ev? od? (sub1 n))))))
  306.  
  307. (f2 11)
  308. ;; #f
  309. (f2 12)
  310. ;; #t
  311.  
  312. ;; I know this is the y-combinator which is a special function from the lambda
  313. ;; calculus that computes fixed points, and that when applied to a function of two
  314. ;; variables can be used to implement recursion, and that when applied to a function
  315. ;; of three variables can be used to implement mutual recursion.  But I don't
  316. ;; understand how it works.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement