LMU | CMSI 386
PROGRAMMING LANGUAGES
Practice Questions
  1. Consider a new EBNF form A ^ B which denotes A | ABA | ABABA | .... Such a form makes it convenient to write rules involving separators, such as
        IDLIST → ID ^ ","
    
    This form can also be used to model a construct representing "one or more" A's, rather than using A+ or AA* or A*A. Show how to do this.
  2. Consider an extension of Iki that features if and while statements, variable declarations, and a large set of arithmetic, relational and logical operators. Variables in this language may only have type integer, but expressions in if and while statements are to be Boolean-valued. Give a syntax for this language that enforces typing. (In other words, type errors are to be syntactic, rather than static semantic, errors.)
  3. Suppose we wanted to modify Iki by (1) making assignments be expressions, as they are in C, rather than statements, (2) adding function declarations and function calls, and (3) allowing four types: integers, booleans, doubles and strings.
    1. Give the macrosyntax (for the whole modified language, not just the parts that have changed). Note that the addition of types means you will need literals for each type as well as additional operators to make use of them. Type checking does not need to be done syntactically.
    2. Write an abstract syntax for the new language.
    3. Write a natural semantics for the new langauge.
    4. Write a denotational semantics for the new langauge.
  4. In the Ada langauge comments are started with "--" and go to the end of the line. Therefore the designers decided not to make the unary negation operator have the highest precedence. Instead, expressions are defined as follows:
        EXP  → EXP1 (and EXP1)*  |  EXP1 (or EXP1)*
        EXP1 → EXP2 (RELOP EXP2)?
        EXP2 → -? EXP3 (ADDOP EXP3)*
        EXP3 → EXP4 (MULOP EXP4)*
        EXP4 → EXPR5 (**  EXPR5)?  |  not EXPR5  |  abs EXPR5
    
    Explain why this choice was made. Also, give an abstract syntax tree for the expression -8 * 5 and explain how this is similar to and how it is different from the alternative of dropping the negation from EXPR2 and adding - EXPR5 to EXP4.
  5. Explain the need for elsif in Perl and Ada. Then propose changes to the Perl and Ada syntax that preserve the syntactic prohibition on the dangling else by means of "blocking" the statement components, but avoids the funny word. Hint: you only have to change the syntax clause for the if-statement; you do not have to add any new clauses.
  6. Give regular expressions for the following languages. You may not use a not operator; use only concatenation, alternation, Kleene star and Kleene plus.
    1. A number from the set {34,36,37,59,57,365}.
    2. The set of all strings over {a,b,c} that do not contain two consecutive a's anywhere in the string.
    3. The set of all strings over {a,b} that do not have two consecutive a's nor two consecutive b's anywhere in the string.
    4. The set of all strings over {a,b,c} that do not contain three consecutive a's anywhere in the string.
    5. The set of all strings over {a,b} that contain neither the substring "aba" nor "babb" anywhere in the string.
  7. C, does not allow structures (i.e., non-atomic objects) to be tested for equality. Ada allows this. How exactly does this complicate the compiler or the runtime system?
  8. If possible, write a program in Modula 3 that makes a variable point to itself. That is, for some designator X, make it so that X^ = X. If this is not possible, state why it is not possible.
  9. If possible, write a program in Ada that makes a variable point to itself. That is, for some designator X, make it so that X.all = X. If this is not possible, state why it is not possible.
  10. Give the abstract syntax tree for the following C++ expression
        (a = 3) >= m >= ! & 4 * ~ 6 || y %= 7 ^ 6 & p
    
  11. Give an abstract syntax tree for the following Ada code fragment:
        if x > 2 or not F(F(X)) then
          Put (-3 * Q);
        elsif not Here or There then
          loop
            while Close loop Try_Harder; end loop;
            X := X ** 3 mod 2*X;
          end loop;
          Q.all(4).G(6) := Person.List(2);
        else
          raise Hell;
        end if;
    
  12. Give the abstract syntax for the following C fragment:
        int f(int x, int y) {
           return y,x?a.p[5]+=7&!y<x<y+++x||+x|*&y-~--y*x^y:0;
        }
    
  13. Give the abstract syntax for the following C fragment:
        printf("%08X %#16.7e\n",x---a.p[9]^x|*&p&1,*((float*)&x));
    
  14. Give the abstract syntax tree for the following Perl fragment (die and split are both list operators):
        die "\n">>1,~\$$x^split @a,3|x**$;=>%p&&&p;
    
  15. Give the abstract syntax tree for the following Perl fragment:
        sub f {
            print $x[0] unless q/pig =~ m!^r$!/ =~ m!^\((4)+!x;
            push @_, sort reverse keys %$shift, g A 1, 2
        }
    
  16. What can't you do with a Perl package named m, s, or y?
  17. In C++ you can say (x += 7) *= z but you can't say this in C. Explain the reason why, using precise, technical terminology. See if this same phenomenon holds for conditional expressions, too. What other languages behave like C++ in this respect?
  18. Identify the following errors as syntactic, static semantic, or dynamic semantic (runtime).
    1. Redeclaration of an identifier in Ada.
    2. Unbalanced parentheses.
    3. Applying an operator to an element of the wrong type in Ada.
    4. Array index out of bounds in Ada.
    5. Integer division by zero in Ada.
    6. Semicolon after a class body in Java.
    7. Wrong number of arguments supplied to a call in Java.
    8. Assignment of a variable of type T to a variable of type subtype of T where the first variable is out of the range of the second in Ada.
    9. An unwanted infinite loop.
    10. Dereference of a null pointer in Ada.
    11. Application of the "." to an identifier which is not a field of the record (or struct) in Ada.
    12. Use of an uninitialized variable in Ada.
    13. for I in A'Range loop Put("*"); X := X+1; end loop; (Ada).
  19. Identify each of the following C90 code fragments as either (a) a lexical error, (b) a syntax error (c) a static semantic error, (d) a dynamic semantic error, or (e) not an error. Again, these are code fragments, not complete programs (translation units, as they call them in C). not complete programs.
    1. {int x; int x;}
    2. {int x; {int x;}}
    3. {int x; struct x {};}
    4. void f() {return 5;}
    5. x +. 2;
    6. x = (((int)x)-*0);
    7. x---!-+~*&x
    8. x = (((int)x)-5));
    9. x>&x
    10. if (x == 2) {return 1;}; else {return 2;};
    11. int a[5]; a[5]=10;
    12. float y; double x = y / 0.0;
    13. Hawai`i = 2;
    14. int y = 0; *y = 7;
    15. main(5, 0, 0, 0, 0, 0); /* a call to the usual main() function */
    16. printf("%d\n", x, y, z);
    17. class C {}
    18. struct {int x;} p; p.y;
    19. int f() {int* p = p;}
    20. int main() {main();}
    21. int f() {int p; return p;}

  20. Identify each of the following as either (a) a lexical error, (b) a syntax error (c) a static semantic error, (d) a dynamic semantic error, or (e) not an error. All items refer to the ML programming language and are considered to be "whole programs" - that is, they have no dependence on external bindings.
    1. let val x = 2 val x = 3 in x end;
    2. let val x = 5; val x = "dog" in x end;
    3. let val x = print 5 in x end;
    4. let val x = print "5" in x end;
    5. let val y = ref 2; val x = while (!y < 1) do print "*" in x end;
    6. let val y = ref 2; val x = while (!y != 1) do print "*" in x end;
    7. let val x = 3 in () end;
    8. let val p = *^$q in () end;
  21. Give a grammars for the languages
    1. {w in {a,b,c}* | w has at most one occurence of any symbol}
    2. {ambncm+n | m,n >= 1 }
  22. Give grammars for the languages
    1. {anbncn | n >= 0 }
    2. {aibjck | i = j or j = k }
    3. {ww | w in {a,b}* }
  23. The language Ada does not require the parameters to a subprogram call to be evaluated in any particular order. Is it possible that different evaluation orders can lead to different arguments being passed? If so, give an example to illustrate this point, and if not, prove that no such event could occur.
  24. It is a well-known irritation that Ada does not allow you to write array aggregates for zero- or one-element arrays, e.g., A := (3) gives a static semantic error when A is a one-element array of Integer. Why is this so? Propose a (trivial) syntactic extension to Ada that would remove this irritation.
  25. What exactly must be the case for a subprogram to not need a static link in its stack frame? Think up as many cases as possible.
  26. In Ada, the declarations
        X: Integer := X + 1;
        Foo: Foo;
        Bar: Real := Bar(Foo);
    
    (where global declarations of X, Foo and Bar are visible) are all illegal, since a declaration of an identifier hides global declarations of the same name immediately at the point it appears in the text, but the identifier may not be used until its declaration is complete. Give an alternate interpretation under which these declarations would be legal and explain the advantages and disadvantages of it from both the programmer's and the compiler writer's perspectives.
  27. In C++ it is not permitted to have two functions that differ only in return type overload each other. In Ada it is allowed. What is the reason for this situation? Even though Ada does allow this flexibility in overloading, the compiler needs some sophistication. What exactly is involved? Be very precise in your explanation and illustrate it with code fragments.
  28. Many programming languages require that in order to have mututally recursive functions, the programmer first define one header (name, return types, parameters and parameter types), then the entire second function, then the entire first function. For example, in C++:
        int f(int x, char y);
        void g(int x) {if (x < 0) f(2, 'c');}
        int f(int x, char y) {g(randomInteger());}
    
    In C++, when f is finally declared, the names of the formal parameters don't have to be repeated exactly as they appeared in the incomplete specification. But in Ada they do. Explain why the Ada rule makes life much easier for the compiler writer.
  29. Here's some code in some language that looks exactly like C++. It is defining two mutually recursive types, A and B.
        struct A {B* x; int y;};
        struct B {A* x; int y;};
    
    Suppose the rules for this language stated that this language used structural equivalence for types. How would you feel if you were a compiler and had to typecheck an expression in which an A was used as a B? What problem might you run into?
  30. Here's a variation of M-J. Dominus' Spectacular Example.
       local
           fun split [] = ([],[])
             | split [h] = ([h], [])
             | split (x::y::t) = let val (s1,s2) = split t in (x::s1,y::s2) end
           fun merge c ([], x) = x
             | merge c (x, []) = x
             | merge c (h1::t1, h2::t2) =
                 if c(h1,h2)<0 then h1::merge c(t1,h2::t2) else h2::merge c(h1::t1,t2);
       in
           fun sort c [] = []
             | sort c x = let val (p, q) = split x
                           in merge c(sort c p, sort c q)
                         end;
       end;
    
    1. During type inference, give the types assigned to
      • the parameter c within sort
      • the function split
      • the function merge
      • the y in the third clause of split?
    2. Give the type of sort and explain why it is not what you would expect.
    3. How do you rewrite the function to make it actually do a mergesort?
  31. Here is a cool little functional language:
        PROGRAM →  (DECL ";")* EXPR
        DECL    →  val ID "=" EXPR
                | fun ID "(" PARAMS? ")" "=" EXPR
        EXPR    →  NUMLIT | ID | UOP EXPR | EXPR BOP EXPR
                | EXPR "?" EXPR ":" EXPR |  ID "(" ARGS? ")" | "(" EXPR ")"
        PARAMS  →  ID ("," ID)*
        ARGS    →  EXPR ("," EXPR)*
        UOP     →  "-" | "abs" | "not"
        BOP     →  "+" | "-" | "*" | "/" | "mod" | "and" | "or" | "==" | "<"
    
    1. Why is this called a functional language?
    2. Is the grammar ambiguous? Why or why not?
    3. Write a Greatest Common Denominator function in this language.
    4. Give three examples of syntax errors and three examples of static semantic errors in this language. Make sure to write down all your assumptions; I did not give you any semantics so you will have to make up something reasonable.
  32. Here is the description of a language. Programs in this language are made up of a non-empty sequence of function declarations, terminated by semicolons, followed by a single expression. Each function declaration starts with the keyword fun followed by the function's name (an identifier), then a parenthesized list of zero or more parameters (also identifiers) separated by commas, then an equals sign, then the body which is a single expression. Expressions can be numeric literals, string literals, identifiers, function calls, or can be made up of other expressions with the usual binary arithmetic operators (plus, minus, times, divide) and a unary prefix negation and a unary postfix factorial ("!"). There is also an infix binary operator called then which indicates that both of its expressions be evaluated in order from left to right, with the value of the right expression being the value of the entire then-expression. There's a conditional expression that looks just like the one in Java, C, and Perl (with the question mark and colon). Factorial has the highest precedence, followed by negation, the multiplicative operators, the additive operators, conditional and finally the then operator. Parentheses are used, as in most other languages, to group subexpressions. Numeric literals are non-empty sequences of decimal digits with an optional fractional part and an optional exponent part, as in Ada but without the fancy "#" notation. String literals are as in C (you did these for Homework #1). Identifiers are those non-empty sequences of letters, decimal digits, underscores, at-signs, and dollar signs, beginning with a letter or dollar sign, that are not also reserved words. Function calls are as in C, with the arguments in a comma-separated list of expressions bracketed by parentheses. There are no comments in this language, and whitespace can be used liberally between tokens.
    1. Write an example program in this language that shows off everything described above.
    2. Write the microsyntax of this language. Use my EBNF extensions for Unicode categories and subtraction.
    3. Write the macrosyntax of this language.
    4. Write a program in this language that consists of the declaration of the GCD function followed by a call to this function with the arguments 99 and 66.
    5. Show the abstract syntax tree for your GCD program
  33. Many languages have a syntax rule
        DESIGNATOR  →  DESIGNATOR  "."  ID
    
    for specifying variables made up from a record and a field of the record. But sometimes it can have the additional interpretation that the DESIGNATOR to the left of the dot was the name of a (visible) subprogram and the ID was an object declared immediately inside that subprogram. Show how to rearchitect the semantic object class hierarchy to support this.
  34. In Ada, C and C++ arrays and records can be allocated on the stack, not just on the heap. When making assigments of aggregates to variables, compilers usually generate code to deposit the values in temporary storage. Why is this necessary in general? After all, in
        Weekdays := Day_Set(False, True, True, True, True, True, False);
    

    we could construct the aggregate directly in the variable Weekdays. Give an example of an assignment statement that illustrates the necessity of constructing an aggregate in temporary storage (before copying to the target variable).

  35. Write a Perl "class" for machine parts that have an identification number (a positive integer divisible by 5), a weight (a positive floating-point number) and a name (which must consist entirely and exclusively of alphabetic characters). Provide a "constructor" that takes in a string consisting of the id, weight, and name, respectively in which The constructor will check for a valid argument by matching against a regex, and if all is cool, will split the string to assign to its fields.
  36. Find some old "procedural" code you have written and rewrite in an object-oriented fashion. I don't mean that you have to use inheritance or polymorphism; all I am really looking for is that you wrap some functions up in a sensible class.
  37. Write a three page paper on the nature of identity in object oriented philosophy. Include some code fragments to illustrate your main points.
  38. In Java, you generally implement "callbacks" via registration of listeners that implement a known interface, rather than using method pointers. Create a Swing component called AngleReader which displays a picture of a circle and notifies all its listeners of the angle, in degrees, that the mouse cursor makes with the horizontal axis of the circle as the mouse moves over it.
  39. A common pattern that comes up a lot is the need to assign unique identifiers to objects of a given class, for example:
        class Item {
            private int id;
            private static int numberOfItemsCreated = 0;
            public Item() {id = numberOfItemsCreated++;}
            // pretend that there are more members here...
        };
    
    As you can see, every item that gets created will get a unique id. Because this pattern occurs frequently, it might be nice to generalize this and make make something reusable out of it so we don't have to write this code inside every class that needs ids. Perhaps we need an interface or abstract class. Tell me why these two suggestions won't work with a detailed, technical answer. Then tell me something that will work. (Note: there is nothing wrong with the access modifiers above; the problems with my two suggestions have to do with the nature of interfaces and abstract classes.)
  40. Given a utility class, can you always rewrite it as a singleton? Given a singleton, can you always rewrite it as a utility class? If so, when would you choose one over the other?
  41. One of the most important ways in which object oriented programming helps us to manage complexity is through the ability to group related classes into a hierarchy of subclasses and superclasses. Dynamic binding (run-time polymorphism) allows us to operate on collections of objects from different classes in a hierarchy safely. Furthermore, systems which are programmed using dynamic binding are more easily extendible.
    1. What construct is used in non-object oriented programming languages to simulate class hierarchies, and why do we say that it is unsafe?
    2. Why are inheritance-like structures in non-OOPLs harder to extend?
  42. Why is it said that implementation inheritance is at odds with encapsulation?
  43. Inheritance is not always appropriate. Discuss the reasons why a design with a superclass Person and subclasses for different jobs (e.g., programmer, manager, ticket agent, flight attendant, supervisor, student, etc.) is a lousy design. Give an alternative.
  44. In designing a class hierarchy in C++, when should you make an operation virtual and when should you make an operation non-virtual? Give examples.
  45. Write C++, Java, Smalltalk, and Ada declarations corresponding to the following class diagram:
  46. Explain how, in C++, you can get access to, and indeed modify, a protected component of an object that someone else declared. As a concrete example, let's say someone has declared
        class C {protected: int x; ...};
        C c;
    

    then your job is to assign a new value to c.x. Assume there are no public operations of C that modify x that you know of. Also, do not use any preprocessor tricks (like #define protected public).

  47. What makes more sense, to inherit a list from a stack or a stack from a list?
  48. Why did the designers of the C++ standard library containers emphatically reject an inheritance hierarchy of containers?
  49. In C++ you can write
        class C {int x;};
        C c;
        C* p = &c;
        cout << p;
    
    and there is no compile-time nor link time error, despite the fact that operator<<(C*) is not a member of ostream (since that class was declared before you declared C), nor for that matter did anyone declare the global function
        ostream& operator<<(ostream&, C*);
    
    So why does it all work? Explain exactly what gets printed and why.
  50. Write a function that takes in a function f and a list [a0, a1, ..., an-1] and returns the list [a0, f(a1), f(f(a2)), f(f(f(a3))), ...].

    For example, if you pass as arguments the function that doubles its inputs, and the list [4, 3, 1, 2, 2], then the return value would be [4, 6, 4, 16, 32].

    Hints: Do the f(f(f...)) as a separate function. Also you do NOT have to make your function tail recursive.

  51. Write a pair of functions, f and g, such that every time you call f, you get back 5 less than the result of the previous call to f or g, and every time you call g, you get back double the absolute value of the result of the last call to f or g. The "initial value" is 0. Hint: I'd use Perl for this one! It is possible to do this in one line of Perl.
  52. Write the following function in Standard ML, where your implementation MUST BE tail recursive.

    Given a list [a0, a1, ..., an-1] return a0*a1 + a2*a3 + a4*a5 + ....

    For example, if given [3, 5, 0, 28, 4, 7] we return 15 + 0 + 28 = 43. If there are an odd number of elements in the list, assume there is an extra "1" for padding.

    Because I am in a good mood, here is a NON tail recursive formulation:

        fun sum_of_prods [] = 0
          | sum_of_prods (x::nil) = x
          | sum_of_prods (x::y::t) = x * y + sum_of_prods t;
    
  53. Write some JavaScript that adds a new method to arrays so that if I call this method on an array with two parameters f and g, I get back a new function which, when called with one argument k, returns the composition of f and g applied to the kth element of the original array. Hint: If we defined the functions square and addSix the "obvious" way, and we called this new method weird, then:
        [4, 6, 7, 3, 5, 2, 4].weird(addSix, square)
    
    would return the function z such that
        z(2) == 55
    
    because the element at index 2 within the array is 7 and 7^2 + 6 = 55.
  54. Complete the following definition of a dot product function in ML:
        val dot =
            let
                fun transpose ... =
            in
                ....
            end;
    
    The transpose function should work like this
        transpose ([x1,...,xn],[y1,..,yn]) = [(x1,y1),...,(xn,yn)]
    
    raising Domain if the arrays have different lengths. The body of the definition of dot (between the "in" and "end") should contain only instances of the functions transpose, o, foldr, map, op*, op+, and the value 0.
  55. What philosophers call a "class" mathematicians call a "set" (i.e., a collection of unordered, unique, values). So, a philosopher says that every member of a subclass is also a member of the superclass. But a C++ programmer says that every member of a superclass is also a member of its subclass! What is going on here?
  56. This fragment of Java code illustrates something about scope. Or does it? Relate it to other similar problems we've seen regarding scope. (Don't forget to come across as being articulate and intelligent in your discussion.)
        public void fail() {
            class Failure extends RuntimeException {}
            throw new Failure();
        }
    
  57. What would this program output under static scope rules? Under dynamic scope rules?
        declare x = 2;
        sub f() {print x;}
        sub g() {declare x = 5; f(); print x;}
        g();
        print x;
    
  58. Explain what is printed under (a) call by value, (b) call by value-result, (c) call by reference, (d) call by name.
        x = 1;
        array y = [2, 3, 4];
        sub f(a, b) {b++; a = x + 1;}
        f(y[x], x);
        print x, y;
    
  59. What's wrong with this, if anything, and why?
        class Pair implements Cloneable {
            private Object first, second;
    
            public Pair(Object x, Object y) {first = x; second = y;}
            public getFirst() {return first;}
            public getSecond() {return second;}
        }
    
  60. Give an example which shows that default parameters are unnecessary in C++ because you can always get the desired effect with overloading.
  61. What does the following program output?
    with Ada.Text_IO, Ada.Integer_Text_IO;
    use Ada.Text_IO, Ada.Integer_Text_IO;
    
    procedure P is
      A: Integer := 4;
      type T is access Integer;
      B: T := new Integer'(4);
      C: T := new Integer'(4);
    
      procedure Q (X: in out Integer; Y: T; Z: in out T) is
      begin
        X := 5;
        Y.all := 5;
        Z.all := 5;
      end Q;
    
    begin
      Q (A, B, C);
      Put (A);
      Put (B.all);
      Put (C.all);
    end P;
    
  62. If possible, give Ada type and object declarations to make a variable point to itself, that is, make it so that P = P.all. If it is not possible to do so, state why no such variable can be defined.
  63. If possible, give C++ type and object declarations to make a variable point to itself, that is, make it so that p == *p. If it is not possible to do so, state why no such variable can be defined.
  64. Consider the implementation of a Container class framework with the following abstract base class Container:
        template <class Item>
        class Container {
        public:
          unsigned numberOfItems() {return currentSize;}
          bool isEmpty() {return currentSize == 0;};
          virtual void flush() = 0;
          ~Container() {flush();}
        private:
          unsigned currentSize;
        };
    

    Here the idea is that each particular (derived) container class shall implement its own flush() operation (which makes sense because different containers are flushed in different ways: there may be arrays, linked lists, rings or hashtables used in the representation), and when a container is destroyed its flush() operation will be automatically invoked. However, the idea is flawed and the code as written causes a terrible thing to happen. What happens?

  65. It is possible to make a Person class, then subclasses of Person for different jobs, like Manager, Employee, Student, Monitor, Advisor, Teacher, Officer and so on. This is a bad idea, even though the IS-A test passes. Why is this a bad idea and how should this society of classes be built?
  66. Why has no (major) language combined static scope and shallow binding?
  67. I couldn't find in the defintion of C whether the language employs shallow or deep binding. Why?
  68. Make a Perl module with a function called nextOdd. The first time you call this subroutine you get the value 1. The next time, you get a 3, then 5, then 7, and so on. Show a snippet of code that uses this subroutine from outside the module. Is it possible to make this module hack-proof? In other words, once you compile this module, can you be sure that malicious code can't do something to disrupt the sequence of values resulting from successive calls to this function?
  69. Write Perl subroutines that takes in a file handle and returns a reference to a hash that maps an integer x to the number of lines in that file that contains x characters (not including the newline), up until the point in the file that contains two blank lines. For example, if the file contains
    Blah
    dog dog 123
    Four
    
    Zero
    1234567890*
    
    
    More stuff!
    1234
    
    Then you should return the hash reference {4=>3,11=>2,0=>1} because, before the part in the file with two blank lines, there are three lines with four characters, two lines with 11 characters, and 1 line with zero characters. The lines after the two blank line sequence are not considered, nor are the lines in the two blank line sequence. If there is no place in the file with two blank lines, then all lines will be counted.
  70. Implement a priority queue data type in Ada, using a server task to provide synchronization.
  71. Implement a priority queue data type in Ada, where each priority queue object is a protected object.
  72. In the example Ada package implementing the Set data type with a guardian task, there is a serious problem with the package design: errors in insertion are not handled well! What happens if we run out of memory? (Answer in terms of the system interfaces.) Show how to add robust error handling to the package and comment on the amount of parallelism permitted with your solution.
  73. Discuss the difficulties of implementing a secure Post Office object in Ada that meets the following requirements. The post office is to maintain a collection of P.O. boxes, each belonging to some task. Any task can put a letter into another task's box, but only the owner of a particular box can open it and read the letters.
  74. A relay is an agent task created by one task to relay a message to another. For example, if a calling task wishes to send a message to another but does not wish to wait for a rendezvous, the caller can create a relay task to send the message. (Note that relays are only appropriate to use when there are no out parameters in the called entry.) Sketch in detail a body for an Ada task T that uses a relay R to call entry E of task U, passing message X.
  75. In Ada, if two tasks are executing a Put procedure at the same time, their outputs may be interleaved. (This could produce amusing and even distasteful results, e.g. writing "sole" in parallel with "ash") Show how to set things up so only one task is writing at a time.
  76. Why do Java programmers not have to worry about the situation in the previous problem (interleaving of text output written to a stream)?
  77. One of the nice features of Quicksort is that it allows a great deal of parallelism in an implementation. After partitioning, the slices on either side of the pivot can be sorted in parallel. It is very easy to set things up to do this in languages such as occam, but tedious in Ada and Java. Code up a parallel version of Quicksort in Ada or Java and explain why it is messy.
  78. In Ada, what happens when you try to call an entry in a task that has terminated? Comment on the following code fragment as a possible approach to calling entry E of task T only if T has not terminated.
        if not T'Terminated then
            T.E;
        end if;
    
  79. In Java, what happens if you invoke a method on a thread that has completed?