JAMES LONG
POSTS TALKS

SICP 2.5

December 14, 2011

Today I'm working through exercise 2.5 from Section 2.1: Introduction to Data Abstraction. The problem sounds intruiging to me.

I'll be honest: I couldn't figure it out. I knew how to do it, but I couldn't figure out the math to solve it. I looked up the solution and it took my a while to understand it. It was interesting enough that I still wanted to write about it!

The problem asks to implement the pair data structure (a container that holds elements x and y) as an integer that represents 2^x * 3^y.

Implementing the cons procedure which creates the pair is easy:

(define (cons x y)
 (* (expt 2 x) (expt 3 y)))

Now we need to implement car and cdr procedures which extract the first and second element. We need to solve for x and y.

I'm not sure what the mathematical solution is, but we can solve it with code. Here is the solution from schemewiki, assuming that expt is an exponent procedure:

(define (count-0-remainder-divisions n divisor) 
 (define (iter try-exp) 
 (if (= 0 (remainder n (expt divisor try-exp))) 
 (iter (+ try-exp 1)) ;; Try another division. 
 (- try-exp 1))) 
 ;; Start at 1, as 0 will obviously pass. 
 (iter 1)) 
(define (car z) (count-0-remainder-divisions z 2)) 
(define (cdr z) (count-0-remainder-divisions z 3)) 

It's a bit confusing, so let's dig through it. The key idea is that 2^x will always produce an even number, while 3^y will always produce an odd number. If we need to find x, we can test each iteration of 2^1 .. 2^n and when the consed integer divided by 2^n produces a remainder, we know that x=n-1 which is the last divisible number. We can do the same thing for y.

Say we have (cons 3 4). Our representation turns into:

(cons 3 4)
(2^3) * (3^4)
(2*2*2) * (3*3*3*3)
(2*2*2) <- always even
(3*3*3*3) <- always odd

An even number is never divisible by an odd number, and vice versa. Knowing this, we can iteratively test values for x or y like this:


;; Test equation, where %= means "the remainder equals"
(2*2*2) * (3*3*3*3) / (2^i) %= 0
i=1
(2*2*2) * (3*3*3*3) / 2 %= 0
(2*2) * (3*3*3*3) / 1 %= 0
;; True
i=2
(2*2*2) * (3*3*3*3) / (2*2) %= 0
2 * (3*3*3*3) / 1 %= 0
;; True
i=3
(2*2*2) * (3*3*3*3) / (2*2*2) %= 0
3*3*3*3 / 1 %= 0
;; True
i=4
(2*2*2) * (3*3*3*3) / (2*2*2*2) %=0
(3*3*3*3) / 2 %= 0
;; False

We know the last equation is false because an odd number is never divisible by 2. So x is i-1 at the point of the last iteration, which is 3. Because we also know that an even number is never divisible by an odd number, you can deduce y the same way.

This works:

(define a (cons 40 76))
(car a)
40
(cdr a)
76

Clever.

Update: A friend pointed out on twitter that it's not so much about even/odd numbers, but about prime factorization. This makes sense because 2 and 3 are prime numbers, and concepts here would work with any primes. Thanks for pointing out the real definition of this!

Join the newsletter

Working on Actual and bootstrapping it. I write about what I'm learning along the way.
I'll never send you spam, I promise. Unsubscribe at any time.

AltStyle によって変換されたページ (->オリジナル) /