In this exercise, we are tasked with creating a procedure last-pair and returns a list containing the last element of the list. It canbe done as follows:-
Exercise 2.18
In this exercise, we are tasked with writing a procedure reverse which can be used to convert a list to another list with the same elements in reverse order. It can be done with the assistance of the append function given in the book.
Exercise 2.19
In this exercise, we are tasked with rewriting the change-counting program in Section 1.2.2 so that they can be used on arbitrary currency by allowing the denominations to be specified as a list. We are given the folowing function cc:-
We are tasked with writing the procedures first-denomination, except-first-denomination and no-more? to complete the procedure. Let us do that and test the function.
We can see that our solution gives the same right answer as before.
Further, we are asked if the order of the list coin-values affect the answer produced by cc or not. The answer is that it should not affect the answer. This is because our current algorithm checks all coin combinations irrespective of their denomination order. Let us test it.
As we can see, the same answer is obtained.
Exercise 2.20
In this exercise, we are tasked with using the dotted-tail notation to define a procedure same-parity which can be used to return a list of all arguments that has the same even-odd parity as the first argument.
Exercise 2.21
In this exercise, we are tasked with creating a procedure square-list by filling the missing expressions in the given procedures. The two procedures perform the operation directly and using map respectively. W
As can be seen, both definitions give the same correct answer. However, the second case would be a better definition because of its simplicity.
Exercise 2.22
In this exercise, we are tasked with determining why the square-list procedure written by Louis Reasoner does not exactly give the right answer.
Original code
The first iteration of the square-list procedure is as follows.
As can be seen, we get the expected result, but in reverse. To see how this happens, let us expand the evaluation.
Thus, to fix this reversal, Louis tries to switch the arguement to cons.
Reversed code
Let us look at the reversed code and its execution.
Now the values appear in the right order but the lists appear wonky. Let us see why.
Thus, we can see that this doesn’t work either because putting nil in the car position does not make it vanish.
Fixed code
Since we have the things in the right order, we can use our old friend append to make things appear as they should.
Thus, we have the right answer by eliminating the nil in the wrong places and flattening the list at each iteration.
Exercise 2.23
We are asked to implement a procedure called for-each in this exercise. This procedure takes a procedure and a list. The procedure does not evaluate to anything but simply perform an action. It can be done using the following code.
Note that begin is used to sequence the operations so that they can be executed together.
Exercise 2.24
In this exercise, we are asked to evaluate the expression (list 1 (list 2 (list 3 4))). Then, we are asked to provide the box-and-pointer structure and the tree interpretation of the result. Evaluating the expression, we get the following:-
The box-and-pointer structure is as follows:-
Likewise, the tree structure is as follows:-
Exercise 2.25
In this exercise, we are tasked with giving the combination of cars and cdrs required to pick 7 from the lists.
(1 3 (5 7) 9)
((7))
(1 (2 (3 (4 (5 (6 7))))))
Note that we can write successive cars and cdrs upto four times using a short-hand notation. Eg. (cadadr x) for (car (cdr (car (cdr x)))) and so on.
Exercise 2.25
This exercise simply tasks us with finding out the result printed when evaluated by the interpreter.
Exercise 2.27
In this exercise, we are tasked with modifying the reverse procedure from before to produce deep-reverse which reverses it elements and reverses all sublists as well.
Let us look at the original reverse procedure.
Similarly, deep-reverse can be defined as follows:-
Let us test it.
Works as predicted. Let us try with a more complex expression.
Exercise 2.28
In this exercise, we are tasked with writing a procedure fringe which takes a tree and returns the leaves arranged left-to-right. It can be accomplished using the following code:-
Let us test the code using the same test cases as the previous exercise.
Exercise 2.28
In this exercise, we are tasked with implementing binary mobile data structures.
A mobile is created by taking two branches and combining them.
Further, a branch is created by compounding a length for the structure and the structure itself, which could be simple number or a mobile.
Branch selectors
Given a mobile, the branches can be selected using the following code:-
With the branch selected, its length and structure can be obtained as follows:-
Weight calculation
The weight of a given mobile can be computed as follows:-
Balance verification
We can verify if a mobile is balanced using the following code:-
Change of representation
When the representation of mobiles are changed to cons instead of list as follows,
Only the accessors need to be changed as follows,
Exercise 2.30
In this exercise, we are tasked with writing square-tree which is analogous to square-list which can be used to square all the elements in a list and maintain the overall structure of the list. Let us look at how to do this directly.
Now, this function could also be rewritten using the map function and recursion. Let us see how this is done:-
Exercise 2.31
In this exercise, we are tasked with creating a procedure tree-map which serves the same purpose for as the map procedure does for lists. Let us define it using the code from the previous exercise as a template.
Exercise 2.32
The subsets function can be defined as follows by filling in the blanks.
The definition of powerset here gives the exact algorithm used by us in our subsets procedure. To prove that this works, let us use the method of induction.
To prove that, let us take two axioms:-
(subset (cons x y)) contains all the members of (subset y)
In (subset (cons x y)), exactly half the members have x, since given any set, there is a 50/50 chance of x being there.
Now, onto the proof:-
The subset of an empty list is a list of empty list. ie (subset ()) is (()).
We assume for a list y, (subset y) gives the correct set of subsets. We add one more element to the set to obtain (cons x y). By axiom 1, (subset y) gives all the subsets of (subset (cons x y)) which does not contain the element x. By axiom 2, (subset (cons x y)) has exactly twice as many members as (subset y). Thus, we get the new set by duplicating the members of (subset y) with x thrown in.
By following this principle, we get the correct subsets function.
Exercise 2.33
In this exercise, we are tasked with rewriting map, append and length functions using accumulate.
map
The map function can be written as follows:-
append
The append function can be written as folows:-
length
The length function can be written as follows:-
Exercise 2.34
In this exercise, we are tasked with evaluating a polynomial at the given value of x using Horner’s Rule which is a recursive procedure which evaluates the polynomial by evaluating it as .
The procedure is defined as follows:-
Exercise 2.35
In this exercise, we are tasked with redefining the count-leaves procedure defined earlier as follows:-
We are told to use the accumulate and map function in the redefinition. For that purpose, let us reuse the enumerate-tree given in the book:-
Exercise 2.36
In this exercise, we need to implement a procedure accumulate-n which takes a list of lists and returns an element-wise accumulation in the form of another list. We assume each sub-list in the argument and the resulting list has the same number of elements each.
Exercise 2.37
In this exercise, we are tasked with implementing matrix operations. The initial dot product procedure is defined as follows:-
Note that it is mentioned in the book that the map procedure used is the original once defined in Scheme which can take an arbitrary number of lists as arguments.
Matrix-vector multiplication
The matrix-vector multiplication can be defined as follows:-
Transpose function
The transpose function can be defined as follows:-
Matrix-matrix multiplication
The matrix-matrix multiplication can be defined as follows:-
All the computations were verified using WolframAlpha
Exercise 2.38
In this exercise we examine fold-left and fold-right procedures.
Other than the fact that fold-left is iterative whereas fold-right is recursive, we can test the difference in results by looking at the examples provided.
As can be seen, we get totally different results for both cases. Let us look at the expansion of each expression,
Now, to get similar operations for both procedures, we need two properties:-
(op initial a) must be the same as (op a initial). This is evident when we try this operation on a list with one element. This property is called commutativity.
(op (op a b) c) must be the same as (op a (op b c)). This is evident when we look at the final form of the expansions. This property is called associativity.
In the given example, the division function and list functions fulfilled neither properties. Let us try with a function that fulfils both cases:-
Exercise 2.39
In this exercise, we are tasked with defining the reverse procedure using the fold-right and fold-left from before.
Exercise 2.40
In this exercise, we are told to define a procedure unique-pairs which takes an integer n and generates a sequence of pairs (i,j) where . Basically, we are told to encapsulate the part that generates the pairs in the given prime-sum-pairs procedure in the book.
We can thus simplify the prime-sum-pairs procedure as follows:-
Exercise 2.41
In this exercise, we need to write a procedure that generates triples consisting of threee integers i, j and k less than or equal to a given number n that sums to a given integer s.
Drawing from the lessons learnt in this section, we start with smaller components and build the solution up. First, we build a unique-triples function that builds triples while using unique-pairs function.
Next, we do our normal filtering using a function.
Exercise 2.42
In this interesting exercise, we need to solve the “eight-queens puzzle”, where we try to place 8 queen pieces on a chess board in an arrangement that no queen is checking any other queen. A resursive solution is proposed which can generate all the solutions to the puzzle.
We are given the main code for solving this problem:-
We are told to finish implementing the required data representations and sub-procedures for this problem.
Representation of positions
The first issue to be dealt with is creating a way to represent the position of a piece on the board. We can use our old knowledge to store the location as a (row, col) pair.
Further, we can represent the state of the board using a list of positions. Empty board can be represented as an empty list. Adding a new piece to the board is as simple as appending it.
Safe positions
Every time we add a new queen piece to the board, we must check if the position is valid, ie, there is no queen checking the new piece. Checking only happens in two cases,
Both pieces are in the same row
Both pieces can be checked diagonally
If both pieces have the same column, we have no trouble.
We can create micro-procedures which check for this.
With that defined, let us look at defining the safe? function.
Putting it all together
Now that all the components are set-up, we can run the code to see the solutions.
As the board size increases, the number of solutions also increase. The exact length is given at OEIS A000170. As can be seen, the first ten solutions areas follows:-
This matches the number in the webpage.
Exercise 2.43
In this exercise, we are told to quantify how slowly Louis Reasoner’s solution witll run compared to the original solution. The only difference is that the order of (queen-cols (- k 1)) and (enumerate-interval 1 board-size) has been reversed. This means that each time the flat-map is called, queen-cols would be called board-size times instead of only once before.
In the original case, for (queen-cols k), (queen-cols (- k 1)) is only called once and work proportional to board-size is done on it. Thus, the overall time complexity is .
However in Louis’ case, for (queen-cols k), (queen-cols (- k 1)) is called board-size times followed by work proportional to board-size done on it. Thus, the overall time complexity is .
Taking the original time taken as T, the new time taken would be .
For this set of exercises, MIT-Scheme was proven insufficient due to the inability to draw pictures. Instead, Racket was used along with the SICP Picture Language package.
This package can be invoked by typing (require (planet "sicp.ss" ("soegaard" "sicp.plt" 2 1))) in the Racket REPL.
Exercise 2.44
In this exercise, we are tasked with implementing up-split, a function analogous to the right-split given in the book. It makes the painter split and branch upwards as follows when (up-split painter n) is called.
The code can be written as follows:-
(paint einstein)
(paint (up-split einstein 1))
(paint (up-split einstein 2))
Exercise 2.45
In this exercise, we are told to express both right-split and up-split as instances of a general splitting operation. It can be done as follows:-
(paint (up-split einstein 2))
(paint (right-split einstein 2))
Exercise 2.46
In this exercise, we are told to simply create a data structure holding a 2D vector. We follow our templates as usual.
Now, to implement the other procedures:-
Let us test them to be sure.
Exercise 2.47
In this exercise, we are given two constructors for frames. With the two constructors, we are needed to provide appropriate selectors for each representation.
Let’s look at the next case.
Exercise 2.48
In this exercise, we are tasked with creating a data structure for representing a segment.
Exercise 2.49
In this exercise, we are told to use the segments->painter function to create shapes.
Outline
The outline of the designated frame can be drawn using four segments. (0,0) to (1,0), (1,0) to (1,1), (1,1) to (0,1) and (0,1) to (0,0).
(paint outline)
Note 0.99 was used instead of 1 so that lines show up in the image.
Cross
The cross can be drawn using just two segments. (0,0) to (1,1) and (1,0) to (0,1).
(paint cross)
Diamond
The diamond can be constructed as follows:-
(paint diamond)
Wave
Drawing the wave takes a bit longer. The coordinates are borrowed from Bill the lizard
(paint wave)
Exercise 2.50
In this exercise, we are tasked with defining the transformations to flip the painters horizontally, rotate them counterclockwise by 180 degrees and 270 degrees.
Let us start with the original coordinate system:-
The dotted outine refers to the area occupied by the frame with a square of length 1.
(paint einstein)
Horizontal flip
A horizontal flip can be done by using the following coordinate system for the frame:-
It can be done by defining the following transformation.
(paint (flip-horizontal einstein))
Rotate counterclockwise by 180 degrees
A rotation by 180 degrees can be done using the following coordinate system:-
It can be done by defining the following transformation.
(paint (rotate180 einstein))
Rotate counterclockwise by 270 degrees
A rotation by 270 degrees can be done using the following coordinate system:-
It can be done by defining the following transformation.
(paint (rotate270 einstein))
Exercise 2.51
In this exercise, we are tasked with writing an operation called below analogous to the beside operation given in the book. This procedure takes in two painters and places them one on top of another.
Direct method
The operation can be performed directly using the following code.
(paint (below einstein einstein))
Using beside and rotation
We can however, also utilise the given beside procedure coupled with rotation to achieve the same effect.
(paint (below einstein einstein))
Exercise 2.52
Through this exercise, we look at changing code at each abstraction level.
Adding smile
To add smile to the wave painter, I took the coordinates again from Bill the lizard.
(paint wave-smile)
Change the corner-split pattern
We are tasked with modifying corner-split so that we only use up-split and right-split once. Let us try the original code:-
(paint (corner-split wave 2))
Now the modified code:-
(paint (corner-split wave 2))
Change of square-limit
We are told to change the square-limit function so that the larger segments are outside. We simply re-order things.