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