SICP in MASK
Publish on 2024-09-26
Stupid modern programmer can’t lint lisp code inside their weak brain, that’s why javascript got the chance to ruin everything.

How many additions are performed when we compute the $n^{th} $ Fibonacci number using the definition of fibs based on the add-streams procedure? Show that the number of additions would be exponentially greater if we had implemented (delay ⟨exp⟩) simply as (lambda () ⟨exp⟩), without using the optimization provided by the memo-proc procedure.

(define fibs
	(cons-stream
		0
		(cons-stream 1 (add-streams (stream-cdr fibs) fibs))))
	
(define (memo-proc proc)
	(let ((already-run? false) (result false))
		(lambda ()
			(if (not already-run?)
				(begin (set! result (proc))
							 (set! already-run? true)
							 result)
				result))))
				
(define (add-streams s1 s2) (stream-map + s1 s2))

He who forget

on the first sight, if you simply draw a graph like below (like I do), this question would be very confusing. shouldn’t It be like this? isn’t the number of addition is always $n-2$ whether we apply memo-proc ?

      fibs: 0  1  1  2 ...
  cdr fibs: 1  1  2  3 ...
            +  +  +  +
fibs: 0  1  1  2  3  5 ...

well, for the evaluation result, it’s right, but wrong when it comes to how it is evaluated.

let’s expand it a little bit.

fibs = [0, 1, add fibs fibs.next] ;; for simplcity, rewrite fibs

;; expand till the third value can be evaluated
fibs = [0, 1, add [0, 1, add fibs fibs.next] [1, add fibs fibs.next]]
;; progress the otter 'add' of the third item (r shift for one item)
fibs = [0, 1, 1, add [1, add fibs fibs.next] [add fibs fibs.next]]

wow, the third value comes out as 1. and it’s obvious that it takes one add operation because we change one add. keep up for the forth value.

;; add fibs fibs.next <=> 1, add [1, add fibs fibs.next] [add fibs fibs.next]
;; above transformation equivalent and takes one add operation
fibs = [0, 1, 1, add [1, add fibs fibs.next] [1, add [1, add fibs fibs.next] [add fibs fibs.next]]]
fibs = [0, 1, 1, 2, add [add fibs fibs.next] [add [1, add fibs fibs.next] [add fibs fibs.next]]]

;; make a variable
f+ = [add fibs fibs.next]
fibs = [0, 1, 1, 2, add f+ [add [1, f+] [f+]]]

;; the first item in f+ is 1, and it is evaluated for two times
;; which brings two add operation
;; fibs = [0, 1, 1, 2, add f+ [add [1, f+] [f+]]]
;;                         ^                ^
;; plus two add should be performed to make the value move to otter space
;; fibs(4) = 3, A(4) = 2+2 = 4

;; so on and so forth

see, we not ‘remember’ each item, instead, each item is decomposed till reach fibs[0] and fibs[1].

for a general case w/o memo-proc, let $A(n)$ be the number of addition for the $n^{th}$ fibs, there is,

$$ A(n) = A(n-1) + A(n-2)+1,\ where~A(0) = 0,\ A(1)=0 $$

$A(2) = A(0) + A(1)+1 = 1$

$A(3) = A(1) + A(2)+1 = 2$

$A(4) = A(2) + A(3)+1 = 4$

$…$

and it will end up result to, in which k is the exponential base.

$$ k^{n-2}>\phi^{n-2},\ where\ \phi=\frac{\sqrt{5}+1}{2} $$

He who memo

the memo version does exactly the one at the very beginning

      fibs: 0  1  1  2 ...
  cdr fibs: 1  1  2  3 ...
            +  +  +  +
fibs: 0  1  1  2  3  5 ...

however, the question is, how the hack does scheme remember it? where does it preserve the value?

© 2024 humbornjo :: based on 
nobloger  ::  rss