sicp-ex-3.8


Anon

The simplest solution to me is based on an observation that the whole expression is essentially supposed to evaluate to the first argument given to f. I.e. when evaluating from the left, we start with 0 and get 0 in the end; when evaluating from the right, we start with 1 and get 1 in the end.

Therefore, we can just return 0 if f has never been called (rendering this call meaningless in the addition expression), and the argument given in the previous call otherwise.

 (define f (let ((second-last-call 0) 
                 (last-call 0)) 
             (lambda (n) 
               (set! second-last-call last-call) 
               (set! last-call n) 
               second-last-call))) 

Tianxiang Xiong

aaa's solution is incorrect.

pluies' solution is nearly correct, except that set! does not return a value.

The following makes use of the simple observation that we can solve the problem if we return the argument of f if we have not called it before and 0 if we have previously called it.

 (define f 
   (let ((called #f)) 
     (lambda (x) 
       (if called 
           0 
           (begin 
             (set! called #t) 
             x))))) 

shyam

Exercise 3.8. When we defined the evaluation model in section 1.1.3, we said that the first step in evaluating an expression is to evaluate its subexpressions. But we never specified the order in which the subexpressions should be evaluated (e.g., left to right or right to left). When we introduce assignment, the order in which the arguments to a procedure are evaluated can make a difference to the result. Define a simple procedure f such that evaluating (+ (f 0) (f 1)) will return 0 if the arguments to + are evaluated from left to right but will return 1 if the arguments are evaluated from right to left.

Solution:

 (define (g y) 
    (define (f x) 
     (let ((z y)) 
        (set! y x) 
        z)) 
    f) 
  (define f (g 0)) 
 The question is given in the context of explaining how the introduction of assignment in procedure produce differences in result when the same procedure is evaluated repeatedly with arguments in different order. i.e to show that calling f twice with arguments in the order (f 0) and then (f 1) is different from (f 1) and (f 0).

Anyway it is pretty obvious that we are to design f with a local state variable in it ;-)

We can write the function in many ways to make the local state variable give the desired result for the above two calls. I Wrote it the way that the local state variable introduces a delayed response. procedure g is used to set the local environment for f. So we can initialize the local state variable as the argument to g. In this case g returns the desired f when called with the argument 0

In order to simulate the left to right and right to left evaluation, we can change the order of arguments to the given + as shown below. It is evaluated in MIT GNU/Scheme. It is seen to be evaluating from right to left Just to be clear, g and f are revaluated after each experiment.

1 ]=> 
;Value: g

1 ]=> 
;Value: f

1 ]=> (+ (f 1) (f 0))

;Value: 0

1 ]=> 
;Value: g

1 ]=> 
;Value: f

1 ]=> (+ (f 0) (f 1))

;Value: 1

To be clearly out of doubt see this:

1 ]=> 
;Value: g

1 ]=> 
;Value: f

1 ]=> (f 0)

;Value: 0

1 ]=> (f 1)

;Value: 0

1 ]=> 
;Value: g

1 ]=> 
;Value: f

1 ]=> (f 1)

;Value: 0

1 ]=> (f 0)

;Value: 1

1 ]=> 

pluies

 (define f 
   (let ((init (- 1))) 
     (lambda (x) (if (= init (- 1)) 
                   (set! init x) 
                   0)))) 

aaa

 (define f  
    (let ((init (/ 1 2)))  
     (lambda (x)  
         (set! init  (- x init)) 
         init))) 

MathieuBorderé

 (define f  
   (let ((state 0)) 
     (lambda (arg) 
       (begin state (set! state arg))))) 
  
 ;;; testing - rename to avoid confusion 
 (define g 
   (let ((state 0)) 
     (lambda (arg) 
       (begin state (set! state arg))))) 
  
 (+ (g 0) (g 1)) 
 Value: 1 
  
 (define h  
   (let ((state 0)) 
     (lambda (arg) 
       (begin state (set! state arg))))) 
  
 (+ (h 1) (h 0)) 
 Value: 0 
  

Dumb solution :-)

 (define left_to_right 666) 
  
 (define (f x) 
   (cond ((= x 0) (begin (set! left_to_right 999) 
                         0)) 
         ((and (= x 1) (= left_to_right 666)) 0) 
         ((and (= x 1) (= left_to_right 999)) 1))) 


<< Previous exercise (3.7) | Index | Next exercise (3.9) >>