# Iteration Exercise - The Summer Show

I have constructed the following scenario to illustrate certain properties of Smalltalk collection classes. I also wish to demonstrate the use of whileTrue: and timesRepeat: iterating selectors.

### Scenario

Summer usually heralds a drop in sales for London's galleries. Their cast of buyers leaves London for more exotic destinations. Similarly, Quad Gallery's owner, Cordelia Pleydell-Bouverie, regularly disappears for a "well earned" six week holiday in Tuscany. However this year she has opted for a break with tradition. In order to tempt the great throng beating its path to the Royal Academy's Summer Show, Cordelia decides to hold her own summer exhibition.

"Small is Beautiful" will consist of small paintings by some of Quad's artists. Cordelia has specified a maximum width of 60 cms for each painting. Furthermore in an attempt to generate sales, she has decided to reduce the price of these paintings by 20 percent. She has asked for a stock list of every painting, by every artist, that matches this description. She also requires that the record of prices, for these paintings, be updated to reflect this reduction.

The gallery holds work in stock by each represented artist. A corresponding record of stock is kept for each artist. Stock consists of paintings, drawings, prints and sculpture. Some artists produce work in all four media, some may simply produce only paintings. Stock records must reflect this diversity. We are going to look at just one artist, Steve Smith. All works of art are considered to be unique. Stock will be added to, or removed, as works of art are sold or returned to the artist.

### Solution

A set is deemed suitable for holding Steve's work currently kept in stock. By using a set, there is no danger of duplication. Steve produces paintings, prints and drawings.

sSmithStock := Set new

We have already seen some of Steve's paintings and drawings

Note that individual prints, from the same edition, are regarded as unique. Here are two of his prints

print3 := Print new.
print3 title: 'Rita';
yearCreated: '2000';
height: 24;
width: 20;
media: 'etching';
price: 500.00;
editionSize: 10;
framedPrice: 650.00

print4 := Print new.
print4 title: 'Rita';
yearCreated: '2000';
height: 24;
width: 20;
media: 'etching';
price: 500.00;
editionSize: 10;
framedPrice: 650.00

print3 = print4 answers with false confirming uniqueness of prints from the same edition. Remember that Print inherits = from Object and is therefore a test for object identity, not state.

>= anObject
object. If = is redefined in any subclass, consider also redefining
the message hash."

^self == anObject

Next, by using add: messages, his work is added to the set.

What is needed now is another collection, but one that contains only those works that match the requirements. This can be achieved by the following

sSmithSmallPs := Set new.
sSmithStock do: [:element | ((element class = Painting)
and: [element width <= 60])

The explanation for this is as follows:

First we need to create another set, sSmithSmallPs, to take all the paintings of the required size.
Secondly we use a do: message to loop through the stock. Each element is accessed in turn and tested for its class with element class = Painting, before testing that the width is less than or equal to 60. The Boolean expression will return either true or false. Note that by using and:, as opposed to &, if the element is not a painting then the argument to and: will not be evaluated. This is a far more efficient means of testing than the use of &, because in the latter case the block will be evaluated even if the receiver is false. One of these two Boolean objects will now act as the receiver to an ifTrue: message. This takes as its argument a block, in this case
If true is the receiver, the block is executed and the element will be added to the new collection.

At this stage all Steve's paintings of 60cms or less in width are held in the set sSmithSmallPs. What is needed is some code that will access all the prices of these paintings and reduce them by 20 percent.

As with many problems there is usually more than one way of arriving at a solution. Here is the simplest

sSmithSmallPs do: [: element | element price: (element price * 0.80)]

Here are two other, lengthy, solutions. I have included them simply to demonstrate a variety of algorithms. The first uses a whileTrue: and the second a timesRepeat: message. In both cases though there is a further problem. A set will not allow us to reference an indexed element by using an at: message. Sets are unindexed and the message at: will cause an error. Help is at hand, because Collection has a number of selectors that allow us to convert collection objects. We need to create an indexed collection in order to use at: and there are several messages that can achieve this including asOrderedCollection. I have chosen this in preference to asArray or asSortedCollection. asArray would constrain the gallery to only those paintings that are currently in stock. Should the gallery request inclusion of other paintings stored in his studio, a fixed size array would prevent this required behaviour. No requirement for sorting has been specified, which rules out asSortedCollection.

|index|
index := 1.
sSmithSmallPs := sSmithSmallPs asOrderedCollection.
[index <= sSmithSmallPs size]
whileTrue: [(sSmithSmallPs at: index)price:
(sSmithSmallPs at: index)price * 0.80.
index := index + 1].
^ sSmithSmallPs

A whileTrue: message can be used to solve any type of looping problem. However caution is needed. The receiver of a whileTrue: is always a block containing a Boolean condition. This condition must, at some point in the looping process, return false. If the block continually returns true this will cause an infinite loop.
In this example a temporary variable index is declared. It is initialised to 1 by the assignment index := 1. The set is sent the message asOrderedCollection and returns an ordered collection that is immediately assigned to sSmithSmallPs
On the first loop the index having value 1 is compared with the size of the collection by writing
index <= steveSmithSmallPaintings size. If this returns true the code inside the argument block is executed.
On the first loop sSmithSmallPs at: index answers with the object at the index of 1. This instance of Painting will receive a price: message. Note that an appropriate message is sent rather than using direct assignment to access the price instance variable. Now price: is passed the argument
(sSmithSmallPs at: index)price * 0.80 i.e. 80% of the current price. Finally, index is incremented by writing
index := index + 1
This enables the value of index to increase with each loop until such time as
index <= sSmithSmallPs size answers with false, at which point the looping will stop.
The expression series will return nil unless you force a return with
^sSmithSmallPs

The next solution uses a timesRepeat: message. The receiver for this message must be an integer object and takes as its argument a block. We can get an integer by sending a size message to the collection. The integer dictates the number of times the block is executed. Again a temporary variable index is used to reference each of the elements.

|index|
index := 1.
sSmithSmallPs := sSmithSmallPs asOrderedCollection.
(sSmithSmallPs size)
timesRepeat: [(sSmithSmallPs at: index)price:
(sSmithSmallPs at: index)price * 0.80.
index := index + 1].
^ sSmithSmallPs

By processing the work of each artist, the gallery can record all the small paintings it holds as stock and readjust their prices.

Finally, sSmithSmallPs can be placed, along with all the other artists' collections, in another set.

smallIsBeautifulSet := Set new.

I have written some questions, together with answers, to test your knowledge of collections. You can find them by following these links:

Previous page » Queue and BuyerQueue

Up to top of page