Lab 6- Blocks & Collections


This lab works with some of the most powerful programming features Smalltalk offers: blocks, collections, and the perform: method.

Block Basics

A block in Smalltalk is a sequence of statements enclosed in square brackets. It can be stored in a variable or passed to other statements as an argument, much like an integer or a string. The statements in a block can be evaluated by sending it

value,
value:,
value:value:,
or value:value:value:,

and specifying the actual parameters for the block to use.

For more details, refer to page 31-33 of your Goldberg/Robson book.

To get started, open up a workspace and try doing a "print it" on each of the following expressions.

Bear in mind that some of these things will not be happy when you ask them to "print it". You will get some that throw error messages and others that complain about various things like "[] in UndefinedObject>>DoIt".

Try to guess what each will do (if they do something besides blowing up in your face) before you print the result, and see if it did what you expect. Understanding these simpler scenarios (why they work or why they don't) will help when you do more complex ones below.

   [ 3 + 4 ] value
   [ 3 + 4 ]
   [ :x | x + 5 ] value: 1
   [ :x | x + 5] value: 7 
   [ :x :y | x + y ] value: 3 value: 4
   [ :x :y :z | x + y / z] value: 8 value: 4 value: 3 

These next three can be tricky...
Look at their complaints for hints about the problem.

   b := [ :x | x - 1 ]
   b value: 5
   b value: 20
   [ [ 2 + 3 ] value ] value
   [ [ 2 + 5 ] ] 

The next three statements done individually will blow up but they
can go together if you pay attention to their complaints. 

   bb := [ [ 2 + 3 ] ]
   bbValue := bb value
   bbValueValue := bbValue value

The Collection Class and Friends...

Simply put a Collection represents a group of objects. The objects that a Collection is comprised of are called elements. The arrays you probably met in CS 1502 are one example of Collections, but Smalltalk has several more.

Here is an overview of some of the collection classes available in Smalltalk:

What Can You Do With a Collection?

A lot! Browse through the abstract Collection and SequenceableCollection classes some time! Because the listing is so long, here are some of the most commonly used methods, for your reference. Don't bother memorizing them, because you can always look them up in System Browser.

adding

   add: newObject
      Include the argument, newObject , as one of the receiver's elements. 
      Answer newObject.

   addAll: aCollection
      Include all the elements of the argument, aCollection, as the 
      receiver's elements. 
      Answer aCollection.

removing

   remove: oldObject
      Remove the argument, oldObject, from the receiver's elements. 
      Answer oldObject unless now element is equal to oldObject, in which 
      case report that an error occurred.

   removeAll: aCollection
      Opposite of addAll.

testing
                   
   includes: anObject
      Answer whether the argument, anObject, is equal to one of the 
      receivers elements.

   isEmpty
      Answer whether the receiver contains any elements.
                   
   occurencesOf: anObject
      Answer how many of the receiver's elements are equal to the argument. 
      anObject.
       
enumerating
                   
   do: aBlock
      Evaluate the argument, aBlock, for each of the receiver's elements.
                   
   select: aBlock
      Evaluate the arguments, aBlock, for each of the receiver's elements. 
      Collect into a new collection like that of the receiver, only those 
      elements for which aBlock evaluates to true. 
      Answer the new collection.
                   
   collect: aBlock
      Evaluate the argument, aBlock, for each the receiver's elements. 
      Answer a new collection like that of the receiver containing the 
      values returned by the block on each evaluation.

accessing
                   
   at: anIndex
      Obtain a value from a Collection.  For Arrays, anIndex is an 
      Integer.  For Dictionaries, it can be anything.  
                   
   at: anIndex  put: anObject
      Replace an element in a collection with something else.

queries
                   
   size
      How many elements are in the collection?

Some Basic Collections

Okay, now you try it. Type the following into a workspace and do it:
   MySet:= Set new.
   MySet add: 'a'.
   MySet add: 2390.
   MySet add: ( Set new )  

Highlight the whole thing and then choose do it. Declare MySet as a global when asked. Then highlight MySet and choose inspect. Peruse around inside of the set for a while. Then select the MySet add: 2390 line and print it. What should have happened? Check the tally to see if you where right.
Mess around by adding some other items to this collection.
Watch the results with the inspect.

Now let's try out a Bag. Enter the following:

   MyBag := Bag new.
   MyBag add: 'a'.
   MyBag add: 2390.
   MyBag add: ( Set new )  

Do the same sequence of operations as you did for Set. Should you get the same results? Mess around by adding some other items to this collection. Watch the results with the inspect.

Now, let's try out an Ordered Collection. [ For the purposes of the this example let's make all the elements the instances of the same class. They don't have to be.]

   MyOrdered := OrderedCollection new.
   MyOrdered add: 'Swimming'.
   MyOrdered add: 'Baseball'.
   MyOrdered add: 'Track&Field'.
   MyOrdered add: 'Gymnastics'  

Again, do it, inspect MyOrdered and observe. Next try the following. In what order will the elements be in?
Mess around by adding some other items to this collection.
Watch the results with the inspect.

   MySorted := SortedCollection new.
   MySorted add: 'Swimming'.
   MySorted add: 'Baseball'.
   MySorted add: 'Track&Field'.
   MySorted add: 'Gymnastics'  

Conversions

There are several methods available for converting a collection from one type to another. Try printing each of these two expressions:

   MyBag asOrderedCollection
   MyBag asSet  

Dictionaries

Sometimes it is useful, convenient, and/or efficient to look data up by a key.

   MyTable := Dictionary new.
   MyTable at: 12345 put: MySorted.
   MyTable at: 12 put: MyOrdered ; at: 505 put: MyBag  

You should know the routine by now... Close the inspector window and then invoke do it on the following.

   MyTable at: MyOrdered put: MySorted  

Objects can serve as keys. After all, numbers are objects too. Reinspect MyTable. You can add to dictionaries this way too.

   MyTable add: (Association key: #aKey value: #aValue )  

Enumeration: Blocks and Collections Together

Collections aren't very useful until you do something with their contents. In Java you might have created a for loop and iterated through an array. In Smalltalk, methods that iterates over a Collection are provided for you, so that you don't have to explicitly do things like create a loop variable.

The most commonly used enumeration method is do:. do: takes one argument, a block, and evaluates that block with each of the elements in the collection in turn. For example, try each of the following, and observe the Transcript:

   MySet do: [:anObj | Transcript show: (anObj) printString. Transcript cr].
   MyBag do: [ :anObj | Transcript show: (anObj printString; cr ]  

do: is sufficient for most enumeration tasks in Smalltalk. However, there are other methods available that can be a little more convenient. For example, try printing the results of these two statements:

   MySorted select: [ :anObj| anObj = 'Baseball' ]
   MyOrdered collect: [ :anObj | anObj = 'Baseball' ]
   MySorted collect: [ :anObj | anObj, ' is a happy String' ]  

For the first, if the block evaluates to true then that element is put a new collection of the same type. For the second, the result of the block is used as the new element in the collection of the same type being created.

Perform

One last topic! Smalltalk let's you call a function just using its name. For example, instead of the simple code "7 squared", Smalltalk allows you to type:
   messageName := #squared.
   7 perform: messageName.  
Try printing the above--does it give you the same as "7 squared"?

There is also a perform:with: method which allows you to give arguments:

   7 perform: #min:  with: 12
   7 perform: #+  with: 12  

So What do you Actually Do?

To show you know how all this works, write blocks to do the following:
  • summationTwo - a block which takes two arguments and returns the sum of 'em
  • averageTwoArgs - a block which takes two arguments and computes their average
  • averageBlock - a block which takes one argument, a collection, and computes the average of every number in the collection. It may assume all elements in the collection are numeric. Hint: there is a sum method already defined.
  • marvinBlock - a block which takes one argument, a collection, and prints either "even" or "odd" to the Transcript for each number in the collection, as appropriate. Hint: there is code above to print every element to Transcript; you can modify it t o produce marvinBlock.
  • doItBlock - a block with three arguments. It sends the third argument as a message to the first argument, with the second argument as an argument. Sounds terribly complicated, but look at the example below.
  • funkyBlock - a block with one argument, a message name. It returns a block with two arguments which will perform the named message on its two arguments. This one actually is a little complicated, but not terribly so.
Your code should work something like this:
   summationTwo := [ "your job..." ].
   averageTwoArgs := [ "your job..." ].
   averageBlock := [ "brilliant averaging code goes here" ].
   marvinBlock := [ "brilliant code for doing a Marvin I/O goes here" ].
   doItBlock := [ "your job" ].
   funkyBlock := [ "your job" ]   

   summationTwo value: 1 value: 3    "should return 4"
   averageTwoArgs value: 1 value: 3    "should return 2"
   averageBlock value: #(1 2 4 5 3)       "should return 3"
   marvinBlock value: #(1 1 4)  "should print 'odd', 'odd', and 'even', to Transcript"
   doItBlock value: 1 value: 3 value: #+   "should return 4"
   doItBlock value: 1 value: 3 value: #max:  "should return 3"  

   b := funkyBlock value: #+.  b value: 1 value: 3.  "should return 4"
   b := funkyBlock value: #min:.  b value: 1 value: 3  "should return 1"  

What to turn in

Copy the definitions of sum2, avg2, avg, etc. from your workspace and paste this text into the BODY of an email message to your TA. Do everything else as normal.


Back to the top | Over to CoWeb