CSE 341 -- Smalltalk Quiz -- Answer Key

November 15, 1995


50 minutes, open book and notes, 60 points total

Your name:


  1. [3 points per subquestion -- 15 points total] What is printed in the transcript after evaluating the following Smalltalk code? If there would be an error, say so.
    1. 
      | k |
      k := 1.
      (k<10) ifTrue:
        [Transcript show: k printString.  Transcript space.  k := k+k].
      
      
      The transcript will show:
      1

    2. 
      | k |
      k := 1.
      (k<10) whileTrue:
        [Transcript show: k printString.  Transcript space.  k := k+k].
      
      
      This gives an error, since booleans don't understand the message whileTrue:.

    3. 
      | k |
      k := 1.
      [k<10] ifTrue:
        [Transcript show: k printString.  Transcript space.  k := k+k].
      
      
      This gives an error, since blocks don't understand the message ifTrue:.

    4. 
      | k |
      k := 1.
      [k<10] whileTrue:
        [Transcript show: k printString.  Transcript space.  k := k+k].
      
      
      The transcript will show:
      1 2 4 8

    5. 
      Transcript show: (1+4 sqrt*3) printString.
      
      
      The transcript will show:
      9.0

      The answer 9 is also acceptable (although not quite what Smalltalk will print).

      The way this works is that the unary sqrt message has precedence over the binary + and * messages, but the two binary messages have the same precedence -- so this is parsed:
      (1+(4 sqrt)) * 3
      Also sqrt returns a floating point result, so the result printed is floating point.

  2. [10 points] Compare the mapcar function in Lisp with the collect: message to collections in Smalltalk. How are they similar? How are they different?

    The mapcar function and the collect: message to collections both involve applying a function or block to each element of a list or collection, and producing a new list or collection of the results. (A block in Smalltalk is like a function in Lisp.)

    The principal differences are that in Lisp the function mapcar works just on lists, while in Smalltalk collect: is understood by all subclasses of Collection -- and they can respond differently if they choose. The normal behavior is that each collection responds to collect: by returning a new collection of the same class as the receiver. Thus sending the collect: message to an instance of Set will return a new set, while sending it to an instance of Array will return a new array.

    A less important difference is that mapcar in Lisp can take multiple lists, and will apply the function to corresponding elements from each list. However, the standard collect: message in Smalltalk just iterates over the single collection that is the receiver. (One could define collect:with:, collect:with:with:, etc. messages that took one or more additional collections, but this isn't part of the standard interface.) You don't need to discuss this less important difference for full credit for your answer, however.

  3. [10 points] Consider the evaluation of the following Smalltalk code.
    | a |
    a := Array new: 3.
    a at: 1 put: 3.5.
    a at: 2 put: (a at: 1) + 4.2.
    (a at: 2) <= (a at: 1) ifTrue: [Transcript show: 'smaller'].
    
    Explain in detail what objects are created, what messages are sent to which objects, and how the control structure works.

    Let's number the lines to make it easier to refer to them:

    1.  | a |
    2.  a := Array new: 3.
    3.  a at: 1 put: 3.5.
    4.  a at: 2 put: (a at: 1) + 4.2.
    5.  (a at: 2) <= (a at: 1) ifTrue: [Transcript show: 'smaller'].
    
    The | a | in line 1 declares a temporary variable a. (This isn't a statement that is executed though.)

    In line 2, we send the message new: to the class Array with the argument 3. This creates a new array of size 3, which is returned and assigned to the variable a.

    In line 3 we send the message at:put: to the object stored in a with two arguments, 1 and 3.5. Since a is an array, it evalutes its method for at:put:, which causes it to change its first element to point to 3.5.

    In line 4 we first send the the array a the message at: with the argument of 1. This gets the first element of a, namely 3.5, and returns it. Then we send the message + with the argument 4.2 to the 3.5. This returns 7.7. Finally we send the message at:put: to a with the arguments 2 and 7.7, thus storing 7.7 in the second element of a.

    In line 5, we get the 2nd and 1st elements of the array a using the two at: messages. Then we send the 2nd element (7.7) the message <= with the argument 3.5. This returns false. The false object then gets the message ifTrue: with the block [Transcript show: 'smaller'] as an argument, and it responds by discarding the block (it's not evaluated in this case).

  4. [10 points] The purpose of this question is to test your understanding of how Smalltalk message lookup is performed. Suppose that we have a class PinballGame that is a subclass of Object, and a class FancyPinballGame that is a subclass of PinballGame.

    PinballGame defines the following methods:

       tiltMessage
         ^ 'TILT!! TILT!! TILT!!'
    
       tilt
         Transcript show: self tiltMessage.
    

    FancyPinballGame defines the following methods:

       tilt
         super tilt.
         SoundGenerator speak: 'call the police!!'.
    
       tiltMessage
         ^ 'This is a highly sophisticated device.  Don''t mess with me!'
    

    What happens when you send the message tilt to an instance of PinballGame? To an instance of FancyPinballGame? (Suppose that SoundGenerator is an object that generates spoken speech.)

    When we send tilt to an instance of PinballGame, TILT!! TILT!! TILT!! is printed in the transcript window. When we send tilt to an instance of FancyPinballGame, This is a highly sophisticated device. Don't mess with me! is printed in the transcript window, and the sound generator says 'call the police!!'.

    Here's how it works. (Your answer doesn't need to include this explanation though.) First we send tilt to an instance of PinballGame. This sends self the message tiltMessage, getting 'TILT!! TILT!! TILT!!', which is printed to the transcript. Then we send tilt to an instance of Fancy PinballGame. This first executes super tilt. This sends the tilt message, but starts the message lookup in the superclass of the class with the method being run, namely PinballGame. This causes the result of sending self tiltMessage to be printed to the transcript. However, when we send self tiltMessage, message lookup starts back in FancyPinballGame, and returns 'This is a highly sophisticated device. Don't mess with me!', which is printed. Then we evaluate SoundGenerator speak: 'call the police!!', causing the sound generator to be activated.

  5. (5 points) Question for both the cautious and the daredevil! Do metaclasses represent a major simplification and improvement of understandability of object-oriented languages? If you think the answer is no, just say "no" and you will get full credit for this question. If you think the answer is yes, say "yes" ... but for credit you must also correctly answer this question:

    The revised new method for Stack class was defined as follows:

    new ^super new setsize: 3 Explain in detail what happens when Stack new is evaluated.

    Finally you can answer "no" to the first question but still answer the second question. You won't get any extra credit but you will get a colorful dinosaur stamp on your quiz if your answer is correct.

    A reasonable answer is just "no". However, here is what happens when Stack new is evaluated.

    We send the message new to the object Stack, which is a class. We look in the class of Stack, which is Stack's metaclass, and find the method

    new ^super new setsize: 3 We run this. First we evaluate super new. This sends Stack the message new, but starts the message lookup in the superclass of Stack's metaclass. Eventually we find the generic new message, and return a new instance of Stack (with nil's in its instance variables). Then we send this instance the message setsize: 3, which initializes it; and we finally return this instance.

  6. (10 points) Tacky but easy-to-grade true/false questions!

    1. Certain methods, such as floating point addition, are implemented as primitives, which invoke operations in the underlying virtual machine.

      true

    2. For compatibility with existing languages, Smalltalk includes C-like syntax for control structures such as conditionals and loops.

      false

    3. An object's instance variables are always accessible to other objects via automatically defined methods that access and set that instance variable.

      false

    4. A framework includes a useful set of classes, designed to be subclassed to implement a particular application.

      true

    5. Class variables are just like instance variables, but hold references to classes rather than instances.

      false