SICP Chapter 1 Part 1

I'm currently attempting to do every problem in Structure and Interpretation of Computer Programs. This post will give solutions to the wordy problems in the firt chapter. You can find the repo where I'm solving the problems https://github.com/IndigoCurnick/structure_interpretation_computer_programs

Exercise 1.5

We are given the following JavaScript code

function p() { return p(); }

function test(x, y) {
	return x === 0 ? 0 : y;
}

Which we then evaluate with

test(0, p());

We are asked what behaviour we get with a applicative-order evaluation vs a normal-order evaluation.

Applicative order is "eager" - this means that all parameters are evaluated before they passed to functions. If we take a look at the following LISP code and consider what happens in applicative order

(define (square x) (* x x))
(square (+ 2 3))

First, (+ 2 3) is evaluated to 5, then passed to the function square.

On the other hand, normal order is "lazy" in that arguments are not evaluated until they are needed. If we consider the LISP code again, the expression (+ 2 3) is passed to the function square and then the whole expression (* (+ 2 3) (+ 2 3)) is evaluated.

If we consider the JavaScript code again, then we can see that p() is a recursive function in the most literal sense - it just returns itself. Therefore, if we are in applicative order when we run test(0, p()) then p() is evaluated, giving p(), which is evaluated giving p() which is...

Therefore, in applicative order, we get an infinite loop.

In normal order, p() is not immediately evaluated, so in that case test will just return 0.

Exercise 1.6

In exercise 1.6, we are again asked a question about normal-order and applicative-order. We write a new kind of function which does conditional evaluation

function conditional(predicate, then_clause, else_clause) {
	return predicate ? then_clause : else_clause;
}

Which can be used as so

conditional(2 === 3, 0, 5); // 5
conditional(1 === 1, 0, 5); // 0

However, consider the following function

function sqrt_iter(guess, x) {
	return conditional(is_good_enough(guess, x),
						guess,
						sqrt_iter(improve(guess, x)), x)
					);
}

What happens when we evaluate this? If the interpreter is applicative-order, then the sqrt_iter function is evaluated before the conditional can be evaluated, leading to an infinite regression.

Exercise 1.9

We are presented with two versions of the plus function

function plus(a, b) {
	return a === 0 ? b : inc(plus(dec(a), b));
}

and

function plus(a, b) {
	return a === 0 ? b : plus(dec(a), inc(b));
}

where

function inc(a) {
	return a + 1;
}

function dec(a) {
	return a - 1;
}

Let's write out how these will evaluate

plus(4, 5);
inc(plus(3, 5));
inc(inc(plus(2, 5)));
inc(inc(inc(plus(1, 5))));
inc(inc(inc(inc(plus(0, 5)))));
inc(inc(inc(inc(5))));
inc(inc(inc(6)));
inc(inc(7));
inc(8);
9;

So the first version is recursive

plus(4, 5);
plus(3, 6);
plus(2, 7);
plus(1, 8);
plus(0, 9);
9;

So the second version is iterative.

Exercise 1.13

In this exercise, we are prove that \(Fib(n)\) is the closest integer to \(\phi^n / \sqrt{5}\), where \(\phi = (1 + \sqrt{5}) / 2\)

First, let's define the following constant (to make life easier)

\[\psi = \frac{1 - \sqrt{5}}{2} \]

We want to prove that

\[Fib(n) = \frac{\phi^n - \psi^n}{\sqrt{5}}\]

We can see for \(n=0,1\) we have the trivial cases

\[ Fib(0) = \frac{\phi^0 - \psi^0}{\sqrt{5}} = 0 \]

\[ Fib(1) = \frac{\phi^1 - \psi^1}{\sqrt{5}} = 1\]

Now consider some \(k \in N, k \geq 2\). Then for \(n = k + 2\)

\[ Fib(k+2) = Fib(k) + Fib(k+1) \]

\[ Fib(k+2) = \frac{\phi^k - \psi^k}{\sqrt{5}} + \frac{\phi^{k+1} - \psi^{k+1}}{\sqrt{5}} \]

\[Fib(k+2) = \frac{\phi^k (\phi+1) - \psi^k (\psi + 1)}{\sqrt{5}}\]

Note that \(\phi\) and \(\psi\) satisfy the following equations

\[ \phi^2 = \phi + 1 \]

\[ \psi^2 = \psi + 1 \]

Therefore, we can write

\[Fib(k+2) = \frac{\phi^k \phi^2 - \psi^k \psi^2}{\sqrt{5}}\]

\[Fib(n) = \frac{\phi^{k + 2} - \psi^{k + 2}}{\sqrt{5}}\]

Therefore, \(Fib(n) = \frac{\phi^n - \psi^n}{\sqrt{5}}\) is true for all \(k \in N\)

Now, we want to prove

\[ \left|\frac{\psi^n}{\sqrt{5}}\right| < \frac{1}{2} \]

At least for all \(n \in N\)

Note that

\[ \frac{ \sqrt{5} - 1 }{2} < \frac{1}{2} \]

\[ | \psi^{n+1} | = | \psi^n | \frac{ \sqrt{5} - 1 }{2} \]

Thus,

\[ | \psi^{n+1} | < | \psi^n | \]

And since

\[ \frac{|\psi^0|}{ \sqrt{5} } < \frac{1}{2} \]

We can conclude that \(\left|\frac{\psi^n}{\sqrt{5}}\right| < \frac{1}{2}\) is true

Therefore,

\[ \left| \frac{\psi^n}{\sqrt{5} } - Fib(n) \right| = \frac{\psi^n}{ \sqrt{5} < \frac{1}{2} } \]

Therefore, \(Fib(n)\) is the closet integer to \(\frac{\phi^n}{\sqrt{5}}\)

Exercise 1.20

In this exercise we are asked to look at the function

function gcd(a, b) {
	return b === 0 ? a : gcd(b, a % b);
}

We're asked to consider how many times the % operator is used in normal order vs applicative order when evaluating gcd(206, 40).

Consider normal order first. The process looks like


gcd(206, 40);

gcd(40, 206 % 40);

gcd(40, 6);

gcd(6, 40 % 6);

gcd(6, 4);

gcd(4, 6 % 4);

gcd(4, 2);

gcd(2, 4 % 2);

gcd(2, 0);

2

So that % is evaluated 4 times.

Now consider applicative order, the process looks like

gcd(206, 40);

gcd(40, 206 % 40);

gcd(206 % 40, 40 % (206 % 40));

gcd(40 % (206 % 40), (206 % 40) % (40 % (206 % 40)));

return (206 % 40) % (40 % (206 % 40)) === 0 ? 40 % (206 % 40) : gcd((206 % 40) % (40 % (206 % 40)), (40 % (206 % 40)) % ((206 % 40) % (40 % (206 % 40))))

(206 % 40) % (40 % (206 % 40)) === 0 ? 40 % (206 % 40) // the first expression evaluates to 0 so only need to consider this part

2

So % is evaluted 6 times

Exercise 1.25

In earlier questions we wrote a function called expmod, here's my Rust implementation

pub fn square(x: i64) -> i64 {
    return x * x;
}

pub fn expmod(base: i64, exp: i64, m: i64) -> i64 {
    if exp == 0 {
        return 1;
    }

    if even(exp) {
        square(expmod(base, exp / 2, m)).rem(m)
    } else {
        (base * expmod(base, exp - 1, m)).rem(m)
    }
}

This question asks why not just write the following function

function expmod(base, exp, m) {
	return fast_expt(base, exp) % m;
}

And you could, and it would work, but in this implementation, for very large inputs, fast_expt would produce a very large output, which might cause performance issues or floating point errors!

Exercise 1.26

In this question someone has written the expmod function with a multiplication rather than calling square

function expmod(base, exp, m) {
	return exp === 0 ? 1 : is_even(exp)
			? expmod(base, exp / 2, m) * expmod(base, exp / 2, m) % m
			: base * expmod(base, exp - 1, m) % m;
}

and asked why this performs so much worse? The issue is that expmod(base, exp / 2, m) is called twice, causing a massive performance impact since the exact same value is calculated twice before being multiplied together. I think some compilers, like the Rust compiler, could optimise that mistake away but generally interpreted languages won't be able to!