In this exercise, Louis Reasoner tries to apply the magnitude function to a complex object given in the section. However, it fails and Alyssa tells him to install the following package to make things work:-
We are asked to explain how this works. As an example, we trace (magnitute z). z is (complex rectangular 3 . 4). It is also assumed that we have installed rectangular-package and polar-package from the previous section to the dispatch table along with the generic selectors for the four functions.
As can be seen, apply-generic is called twice. The first time it is applied, the additions to the complex package as suggested would cause the 'complex tag to be peeled off and another magnitude function applied to the rectangular object inside. Since we had already defined the precedure for the rectangular object, that is evaluated and we get the right answer. The same will be done for a polar object wrapped inside.
Exercise 2.78
In this exercise, we are told to modify the definitions of type-tag, contents and attach-tag from the previous section so that we can simply store a scheme-number as a Lisp number without tags. We can do that using the following code:-
Thus, by using primitive predicate function number?, we do not need to tag numbers.
Exercise 2.79
In this exercise, we are tasked with implementing equ? which can be used as a generic function to test the equality of two numbers which can be ordinary numbers, rational numbers or complex numbers.
Installing the above code would allow equ? to compare the three types of numbers as follows:-
Exercise 2.80
Similar to equ? in the previous exercise, we are told to implement =zero?, a generic function that checks if an ordinary number, a rational number or a complex number is zero.
Now, we can check if any type of number is zero as follows:-
Exercise 2.81
This exercise serves to introduce the concept of coercion to us. Louis believes that we need to add functions to coerce arguments of each type to their own type ie. an identity function. With the identity functions installed, we are asked to explain its effects.
Part-1
In the first part of the question, Louis installs the following code in addition to the identity coercions:-
Now, we are asked what happens if we call exp on two complex numbers. Since we do not have a exp procedure defined for '(complex complex) type, I would expect it to enter an infinite loop. Let us see how this is:-
As can be seen, we enter an inifinite loop because coercing back to the original type brings us back to square one.
Part-2
In the second part, we are asked if Louis made the right choice in providing coercions for the arguments of the same type. My answer would be no. Louis made the wrong decision. As seen in the previous part, adding identity coercions cause loops because they add no new information to the program flow. If there already did not exist a function that takes the argument types, trying again without changing types would be foolish. By not adding these identity coercions, apply-generic function would work correctly by throwing an error for the missing function.
Part-3
In this part, we are told to modify apply-generic so that it does not try coercion if both arguments have the same type.
Exercise 2.82
In this exercise, we are tasked with generalizing apply-generic to handle coercion in the general case of multiple arguments. This can be done by finding a super-type amongst the given argument types and coercing them all to the proper form. To that end, let us rewrite apply-generic in a more composed form as follows:-
When we cannot find the correct function, we try coercing. First, we check if all the supplied types are the same. If they are, there is no use coercing and emits an error. Then, we try to find a super type amongst the given types. If we can’t find such a type, we emit an error. Finally, given the super-type, we coerce all the arguments to that super-type and try again. Let us run some test cases:-
The original cases work. Let us define a function that takes three arguments and try again.
The one pitfall to this method is that the super-type to be tested is always one that is amongst the arguments supplied. For example, let us look at the relations for geometric figures given in the book. If we are given a function called on '(triangle quadrilateral) type, we cannot solve it correctly because there is no coercion from 'triangle to 'quadrilateral or vice versa. However, there exists coercion for their combined super-type 'polygon. This coercion will not be tested in our current algorithm.
Exercise 2.83
In this exercise, we are asked to design a procedure that raises the type of the data and install them. We use scheme-number to replace real.
With this package in place, we can raise the data types. Let us test it:-
Exercise 2.84
This exercise tasks us with using the raise operation defined in the previous exercise to modify the apply-generic procedure to work on arguments of multiple type. We can do that by testing which of the provided types is higher up in the tower of type hierarchy and raising the other to that type.
Further, we are required to set-up code such that future modifications are easy. We assume that the types follow a tower hierarchy and not a more complex relationship like the polygons. Let us first define how we can define the type hierarchy
Thus, when we add new types, we only need to modify the tower and add the 'level definition to the dispatch table. Now, we can modify the apply-generic function.
Exercise 2.85
In this exercise, analogous to the raise operation that we have worked with, we are tasked with implementing a drop operation that can be used to push the data to the simplest level in the hierarchy. For that purpose, we create a function called project that pushes the data down by throwing away data. When we raise it again, if it retains the same value before projecting, we can safely use the lower level for storage.
Let use first define the project procedure:-
Now that we have successfully implemented project, let us implement the drop procedure.
As can be seen, drop works perfectly. Now, let us integrate it into the apply-generic procedure.
Exercise 2.86
In this exercise, we are told to modify complex numbers so that their components can be of any numerical type that we desire in the system. Looking at the code for computing these values, we have are required to implement these new functions for the components:- sin, cos, atan, square and sqrt. We implement them for the rational and real numbers and modify the packages to use generic functions.
Once we have defined these, we can rewrite the complex number packages. Note we have used different names so as not to overwrite default functions.
We also modify apply-generic to drop type when these functions are called.
Let us test the code:-
We now get answers of the right type.
Exercise 2.87
In this exercise, we are tasked with implementing a =zero? function for polynomials that can be used to determine if a polynomial is zero. We can simply reuse the functions given in the book. Let us add that to the generic arithmetic package.
Let us test this:-
Exercise 2.88
In this exercise, we are tasked with implementing the subtraction of polynomials. Based on the hint given, we could simply negate the coefficients of the subtrahend and reuse the add function written for the polynomial. Let us first implement the negate function.
With the negation implemented and tested, we can extend the polynomial system to include subtraction of polynomials:-
Exercise 2.89
In this exercise, we are tasked with implementing a dense representation of polynomial terms as opposed to the sparse representation provided in the book along with the required procedures. In the sparse representation, we just store the coefficients of the polynomial in the order that they appear in without the power terms.
The method proposed should maintain the same external interface as the dense representation that is used by the polynomial package to access the coefficients. We should also traverse the list while emitting the actual order the coefficients belong to. To do this, we need to add one more item that refers to the order of the first coefficient in the list.
We also place a new negation function for the dense term-list:-
Let us test it out:-
Exercise 2.90
In this exercise, we are tasked with implementing a polynomial system that can work with both dense and sparse representation simultaneously. We can coerce between each representation as needed. The coefficients can either be a number or a polynomial.
Note:- Since our old number tower is beyond the scope of upcoming exercises, we stick with the simple default numbers provided in MIT-Scheme so as to keep things simple. There will be no drop or raise being done. Only direct coercion. Full capability can be achieved but required too much testing corner cases to be use for this exercise.
Let us test it.
As can be seen, we can use both representations interchangably because of the similar interface they expose. Taking terms from them emits the same data structure, so coercion is not even needed. The result of mixing is of the type of the first argument.
Exercise 2.91
In this exercise, we are tasked with implemeting polynomial division. For that purpose, we are given a skeleton code that we should complete.
By default, we have set things up to use the dense representation.