Assigned: Friday, January 29, 1998
Due: Friday, February 5, 1998
The purpose of this assignment is to help you think about language design issues from a variety of perspectives.
1. Language Evaluation
Choose a language that you know and use. Pick eight criteria from those given in lecture, the book, or Hoare's article and evaluate the language according to those criteria. That is, for each criterion, evaluate how well or how poorly the language meets that criterion, motivate your evaluation, and, if possible, give an example that supports your argument.
You should not repeat examples given in Louden or in class.
In general, your answers were reasonable. Some answers did not go into enough detail. ``XXX is reliable'' is not a sufficient answer for this question. I was also surprised to see that few of you gave specific examples (I didn't penalize you; I was just surprised). A few of you used criteria that we didn't discuss but that came from the white paper for a specific language.
I was surprised to see that some of you claimed that C and C++ are portable. C (at least as used by many programmers) is surprisingly non-portable, since different machines will have different sizes for the basic types. Since many C programmers freely convert between integers and types (at least on machines in which they have the same size), many programs break when moving to other platforms. (Having tried to port many programs in different languages, I'm generally suspicious of any claims of portability.)
Most of you got a ``Hah!'' for at least one comment. In part, this is because many of you seemed to feel that you should be advocates for particular languages, and therefore exaggerated particular features.
Most of you have a fairly primitive understanding of extensibility. A truly extensible language would make it easy to add whole new language structures, such as new control structures.
Last year, about half of the class did Java, and a few did Pascal. Other languages covered included Basic, Maple, C, and Common LISP. This year, there was more variation. People did C (three people), C++ (two people), Delphi (cleverly choosing a language I don't know), Java (three people), Pascal (two people), Perl (two people), and QuickBasic (another language I don't know). Note that when you say C, you should make it clear whether you mean ANSI C (standardized) or the original C (traditionally called ``K&R C'').
2. Iteration vs. Recursion
Write a recursive and an iterative greatest common divisor function in Java (or Pascal, if you don't know Java). Write a program (or programs) that determines which is faster and by how much. If there a reason, other than execution speed, that you might prefer one function to the other?
Note that Louden presents Modula-2 code for both the iterative (page 9) and recursive (page 13) versions of the function.
I report on possible answers pertaining to speed below.
Why might you prefer one to the other? In a naive compiler or interpreter, the recursive one will take more system resources. (It's a tail-recursive function, so a smart compiler should be able to eliminate the need for additional stack space.) However, the iterative one involves an additional temporary variable that some might find unclear. Some people tend to think recursively and others tend to think iteratively, making one or the other more ``natural'' for different types of people.
Last year, most students wrote Java programs to answer this question. A few used Pascal and one used C. There were a surprisingly large variety in your answers. The C program showed no difference when optimization was turned on (presumably because the tail-recursive GCD was converted to equivalent iterative code). One person reported that the recursive method took 2/3 of the time of the iterative method. Most reported that the recursive method was slower than the iterative method, with a few finding it taking 3 times as long as the iterative method.
Here are this year's results.
|Language||Iterative X times faster||Recursive X times faster||About the same||Note|
|Delphi||X||Difficult to time.|
|Java||variable amount||Included timing of output|
|Java||3 to 20||Included timing of output|
|Java||30||No code provided|
|Java||X||Included timing of output|
|Java||10||Included timing of output|
|Java||Nearly infinite (10 seconds vs 0)|
|Pascal||X||Included timing of output|
|Pascal||3||Included timing of output|
Even though this was a short program, I expected to see good coding habits, including comments, appropriate variable names, and modularity. Most of you know this about me by now, so I had few problems with your assignments.
I frequently saw little real thought put into the testing. For example, some people timed input and output in addition to computation and many timed only one call to each method, which is often so quick as to give unreliable timings. The better tests called each version repeatedly, often with different parameters. The best tests showed some consideration of appropriate parameters, including randomly generated parameters. if you used randomly generated parameters, it was important to ensure that both methods were called with the same parameters.
Almost no one attempted to consider which inputs are particularly hard for a GCD algorithm. A typical example of 12 and 8 requires only two steps to find the GCD. It is not clear that randomly generated numbers require significantly more steps.
What are the worst pairs we can use? The Fibonacci numbers. Note that the GCD algorithm, when given two subsequent Fibonacci numbers, will produce the Fibonacci sequence in reverse. Since we never reduce the pair by more than one times the smaller number, we will have particularly many repetitions.
A few of you neglected to comment on your results or to suggest which method was clearer. Surprisingly, those who found recursive faster noted that the iterative method was clearer and vice versa.
Some of you did awful timing. For example, you might have only tried one repetition of the algorithm, inserted a delay loop into the functions (why, I don't know), or even timed the amount of time it took to generate output, as in
before = System.currentTimeMillis(); System.out.println("The iterative GCD of " + a + " and " + b + " is " + GCD.iterative(a,b)); after = System.currentTimeMillis(); System.out.println(" Computation took: " + (after-before) + " milliseconds.");
In his paper, Hoare promotes the use of operator overloading, in which programmers can define the meaning of the standard operators on new data types. However, the experience of the C++ community (which permits operator overloading) has illustrated some disadvantages of operator overloading. Describe some disadvantages of operator overloading.
The biggest problem with operator overloading is that there is rarely a natural meaning to the operators except in a few cases. For example, what does it mean to add, multiply, or divide two stacks.
Even in cases in which you're dealing with structures that are typically added, multiplied, or divided, there may still be some ambiguity. For example, there are at least two different senses of vector multiplication.
Because C (and therefore C++) programmers seem to believe that ``brevity is the soul of programming'', many programmers overload operators when it is completely inappropriate to do so.
In some cases, other arithmetic properties, such as associativity,
commutativity, and distributability, can be compromised. For example,
in many cases it may not be that
a+b is the same as
b+a. If you program in Java, you may realize that
a+(b+c) may not give the same result as
(a+b)+c, particularly if one of the values is a string.
A few of you reported on overloading in general. The question was intentionally phrased to highlight a particular type of overloading.
A few of you hadn't had much experience with overloading. While I would have preferred that you explored more on your own, I tried not to penalize you inappropriately. (As the term progresses, the ability to successfully use ``that's not in my background'' as an excuse will rapidly diminish.)
4. Coercion in Pascal and Java
In Pascal and Java, integers can be assigned to real variables, but reals cannot be assigned to integer variables. What design principle does this violate? Would it be a good idea to allow reals to be assignable to integer variables? Why or why not? [Modified from Louden, p. 59, q. 5]
This is a violation of the principle of uniformity. Some might also say that it's a violation of orthogonality or generality. As some noted, these violations can influence both readability and writability
Why is it a bad idea to convert reals to integers? A common answer is that there is a loss f procession. A more significant problem is that we have to formally define what the conversion means for a wide variety of situations. In particular, truncation is dangerous, since approximation is often used for real numbers. Hence, we might find that the following code prints 1 instead of 0.
var i,j: integer; var r: real; i := some large integer; r := i; j := r; write(i-j);
It is also likely that we will encounter similar problems when rounding. Even when there is no fractional part, this may happen if the representation of reals uses fewer bits for the non-exponent part.
Importantly, real numbers and integers may cover different ranges (with real numbers traditionally covering wider ranges, but with less precision). By assigning reals to integers, we will need to worry about overflow.
There may also be a loss of precision in conversion from integer to real. Consider the following Java code fragment (even if you don't read Java, you should be able to understand the fragment).
float f1 = Long.MAX_VALUE; float f2 = Long.MAX_VALUE-1; System.out.println(f1-f2);
What do you think this prints? If you said ``1.0'', you're wrong. If you said ``0.0'', you're right.
Is there a better solution? Perhaps to require the same specifications
in both direction. That is, don't coerce integers to reals. Rather,
5. Simplifying a Language
Choose a feature that you think should be removed from some programming language (you can choose which language). Why should this feature be removed? What problems might arise as a result of this removal? [Modified from Louden, p. 59, q. 6]
You should not repeat examples given in the readings or in class.
6. Extending a Language
Choose a feature that you think should be added to some programming language (you can choose which language). Why should this feature be added? What needs to be stated about the feature for those implementing or using the language to understand it unambiguously? [Modified from Louden, p. 59, q. 7]
You should not repeat examples given in the readings or in class.
7. Orthogonality and Uniformity
Find definitions of orthogonality and uniformity other than those in my notes and Louden. Then, using the various definitions, summarize how these two concepts are similar and different.
Enough of you had problems with this that I did not grade it. Sorry, I'm not going to give you a definitive answer. This was one of those ``The goal is for you to think about it'' questions.
Disclaimer Often, these pages were created ``on the fly'' with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.
This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS302/99S/Assignments/notes.01.html
Source text last modified Mon Feb 8 12:21:50 1999.
This page generated on Mon Feb 8 12:27:19 1999 by SiteWeaver. Validate this page.
Contact our webmaster at email@example.com