Smalltalk80LanguageImplementation:Chapter 10

From 흡혈양파의 번역工房
Revision as of 09:18, 9 May 2014 by Onionmixer (talk | contribs) (정오표 수정)
Jump to navigation Jump to search
Chapter 10 Hierarchy of the Collection Classes

Hierarchy of the Collection Classes

Object

    Magnitude
        Character
        Date
        Time

        Number
            Float
            Fraction
            Integer
                LargeNegativeInteger
                LargePositiveInteger
                SmallInteger

        LookupKey***
            Association***

    Link***

        Process

    Collection

        SequenceableCollection***
            LinkedList***

                Semaphore

            ArrayedCollection***
                Array***

                Bitmap
                    DisplayBitmap

                RunArray***
                String***
                    Symbol***
                Text***
                ByteArray***

            Interval***
            OrderedCollection***
                SortedCollection***
        Bag***
        MappedCollection***
        Set***
            Dictionary***
                IdentifyDictionary***

    Stream
        PositionableStream
            ReadStream
            WriteStream
                ReadWriteStream
                    ExternalStream
                        FileStream

        Random

    File
    FileDirectory
    FilePage

    UndefinedObject
    Boolean
        False
        True

    ProcessorScheduler
    Delay
    SharedQueue

    Behavior
        ClassDescription
            Class
            MetaClass

    Point
    Rectangle
    BitBit
        CharacterScanner

        Pen

    DisplayObject
        DisplayMedium
            Form
                Cursor
                DisplayScreen
        InfiniteForm
        OpaqueForm
        Path
            Arc
                Circle
            Curve
            Line
            LinearFit
            Spline


Figure 10.1 provides a road map for distinguishing among the various collection classes in the system. Following the choices in the figure is a useful way to determine which kind of collection to use in an implementation.

그림 10-1


One distinction among the classes is whether or not a collection has a well-defined order associated with its elements. Another distinction is that elements of some collections can be accessed through externally-known names or keys. The type of key defines another way of distinguishing among kinds of collections. Some are integer indices, implicitly assigned according to the order of the elements; others are explicitly assigned objects that serve as lookup keys.


One unordered collection with external keys is a Dictionary. Its keys are typically instances of String or LookupKey; the comparison for matching keys is equality (=). Dictionary has a subclass, IdentityDictionary, whose external keys are typically Symbols. Its comparison for matching keys is equivalence (= =). Elements of a Bag or a Set are unordered and not accessible through externally-known keys. Duplicates are allowed in a Bag, but not allowed in a Set.


All ordered collections are kinds of SequenceableCollections. Elements of all SequenceableCollections are accessible through keys that are integer indices. Four subclasses of SequenceableCollection support different ways in which to create the ordering of elements. An additional distinction among the SequenceableCollection classes is whether the elements can be any object or whether they are restricted to be instances of a particular kind of object.


The order of elements is determined externally for OrderedCollections, LinkedLists, and ArrayedCollections. For OrderedCollection and LinkedList,the programmer's sequencefor adding and removing elements defines the ordering of the elements. An element of an OrderedCollection can be any object, while that of a LinkedList must be a kind of kink. The different ArrayedCollections in the system include Array, String, and ByteArray. The elements of an Array or a RunArray can be any kind of object, elements of a String or of a Text must be Characters, and those of a ByteArray must be SmallIntegers between 0 and 255.


The order of elements is determined internally for Intervals and SortedCollections. For an Interval, the ordering is an arithmetic progression that is specified at the time the instance is created. For a SortedCollection, the ordering is specified by a sorting criterion determined by evaluating a block known to the collection. Elements of an Interval must be Numbers; elements of a SortedCollection can be any kind of object.


In addition to the collection classes already mentioned, MappedCollection is a Collection that represents an indirect access path to a collection whose elements are accessible via external keys. The mapping from one set of external keys to the collection is determined at the time the MappedCollection is created.


The remainder of this chapter explores each of the collection subclasses, describing any additions to the message protocols and providing simple examples.


Class Bag

A Bag is the simplest kind of collection. It represents collections whose elements are unordered and have no external keys. It is a subclass of Collection. Since its instances do not have external keys, they cannot respond to the messages at: and at:put:. The message size answers the total number of elements in the collection.


A Bag is nothing more than a group of elements that behaves according to the protocol of all collections. The general description of collections does not restrict the number of occurrences of an element in an individual collection. Class Bag emphasizes this generality by specifying an additional message for adding elements.

adding
add: newObject withOccurrences: anInteger Include the argument, newObject, as an element of the receiver, anInteger number of times. Answer the argument, newObject.
Bag instance protocol


Consider the example class Product which represents a grocery item and its price. A new Product may be created using the message of: name at: price, and the price of an instance is accessible by sending it the message price. Filling one's grocery bag may be expressed by

sack  Bag new.
sack add: (Product of: #steak at: 5.80).
sack add: (Product of: #potatoes at: 0.50) with Occurrences: 6.
sack add: (Product of: #carrots at: 0.10) withOccurrences: 4.
sack add: (Product of: #milk at: 2.20)


Then the grocery bill is determined by the expression

amount  0.
sack do: [ :eachProduct | amount  amount + eachProduct price]


or

sack inject: 0
    into: [ :amount :eachProduct | amount + eachProduct price]


to be $11.40. Note that the messages add:, do:, and inject:into: to a Bag are inherited from its superclass, Collection. A Bag is unordered, so that, although enumeration messages are supported, the programmer cannot depend on the order in which elements are enumerated.


Class Set

Class Set represents collections whose elements are unordered and have no external keys. Its instances cannot respond to the messages at: and at:put:. A Set is like a Bag except that its elements cannot be duplicated. The adding messages add the element only if it is not already in the collection. Class Set is a subclass of class Collection.


Classes Dictionary and IdentityDictionary

Class Dictionary represents a set of associations between keys and values. The elements of a Dictionary are instances of class Association, a simple data structure for storing and retrieving the members of the key-value pair.


An alternative way of thinking about a Dictionary is that it is a collection whose elements are unordered but have explicitly assigned keys or names. From this perspective, the elements of a Dictionary are arbitrary objects (values) with external keys. These alternative ways of thinking about a Dictionary are reflected in the message protocol of the class. Messages inherited from class Collection--includes:, do:, and other enumeration messages--are applied to the values of the Dictionary. That is, these messages refer to the values of each association in the Dictionary, rather than to the keys or to the associations themselves.


Messages inherited from class Object--at: and at:put:mare applied to the keys of the Dictionary. The at: and at:put: paradigm is extended for the associations and the values by adding messages associationAt: and keyAtValue:. In order to provide additional control when looking up elements in a Dictionary, the message at:ifAbsent: is provided; using it, the programmer can specify the action to take if the element whose key is the first argument is not found. The inherited message at: reports an error if the key is not found.

accessing
at: key ifAbsent: aBlock Answer the value named by the argument, key. If key is not found, answer the result of evaluating aBlock.
associationAt: key Answer the association named by the argument, key. If key is not found, report an error.
associationAt: key ifAbsent: aBlock Answer the association named by the argument, key. If key is not found, answer the result of evaluating aBlock.
keyAtValue: value Answer the name for the argument, value. If there is no such value, answer nil. Since values are not necessarily unique, answer the name for the first one encountered in the search.
keyAtValue: value ifAbsent: exceptionBlock Answer the key for the argument, value. If there is no such value, answer the result of evaluating exceptionBlock.
keys Answer a Set containing the receiver's keys.
values Answer a Bag containing the receiver's values (includes any duplications).
Dictionary instance protocol


As an example of the use of a Dictionary, suppose opposites is a Dictionary of word Symbols and their opposites.

opposites  Dictionary new.
opposites at: #hot put: #cold.
opposites at: #push put: #pull.
opposites at: #stop put: #go.
opposites at: #come put: #go


Alternatively, an element can be added using the message add: by creating an Association as the argument.

opposites add: (Association key: #front value: #back).
opposites add: (Association key: #top value: #bottom)


The Dictionary, opposites, now consists of

key value
hot cold
push pull
stop go
come go
front back
top bottom


We can use the testing protocol inherited from class Collection to test the values in the Dictionary. Notice that includes: tests the inclusion of a value, not a key.

expression result
opposites size 6
opposites includes: #cold true
opposites includes: #hot false
opposites occurrencesOf: #go 2
opposites at: #stop put: #start start


The fourth example indicates that, although a key can appear only once in a Dictionary, a value can be associated with any number of keys. The last example re-associates the key #stop with a new value, #start. Additional messages are provided in class Dictionary for testing associations and keys.

dictionary testing
includesAssociation: anAssociation Answer whether the receiver has an element (association between a key and a value) that is equal to the argument, anAssociation.
includesKey: key Answer whether the receiver has a key equal to the argument, key.
Dictionary instance protocol


Then we can try

expression result
opposites includesAssociation:(Association key: #come value: #go) true
opposites includesKey: #hot true


Similarly, the removing protocol specified in class Collection is extended to provide access by reference to associations and keys, as well as to values. However, the message remove: itself is not appropriate for a Dictionary; in removing an element, mention of the key is required.

dictionary removing
removeAssociation: anAssociation Remove the key and value association, anAssociation, from the receiver. Answer anAssociation.
removeKey: key Remove key (and its associated value) from the receiver. If key is not in the receiver, report an error. Otherwise, answer the value associated with key.
removeKey: key ifAbsent: aBlock Remove key (and its associated value) from the receiver. If key is not in the receiver, answer the result of evaluating aBlock. Otherwise, answer the value named by key.
Dictionary instance protocol


For example

expression result
opposites removeAssociation: (Association key: #top value: #bottom) The association whose key is #top and value is #bottom. opposites has one less element.
opposites removeKey: #hot The association whose key is #hot and whose value is #cold. This association is removed from opposites.
opposites removeKey: #cold ifAbsent: [opposites at: #cold put: #hot] hot


As a result of the last example, the association of #cold with :#:hot is now an element of opposites.


The message do: evaluates its argument, a block, for each of the Dictionary's values. The collection enumerating protocol, inherited from class Collection, is again extended in order to provide messages for enumerating over the associations and the keys. Messages supporting uses of reject: and inject:into: are not provided.

dictionary enumerating
associationsDo: aBlock Evaluate aBlock for each of the receiver's key/value associations.
keysDo: aBlock Evaluate aBlock for each of the receiver's keys.
Dictionary instance protocol


We thus have three possible ways of enumerating over a Dictionary. Suppose newWords is a Set of vocabulary words that a child has not yet learned. Any word in opposites is now part of the child's repertoire and can be removed from newWords. Evaluating the following two expressions removes these words (the first removes the values, the second the keys).

opposites do: [ :word | newWords remove: word ifAbsent: []].
opposites keysDo: [ :word | newWords remove: word ifAbsent: []]


Note that if a word from opposites is not in newWords, then nothing (no error report) happens. Alternatively, one expression, enumerating the Associations, can be used.

opposites associationsDo:
    [ :each |
        newWords remove: each key ifAbsent: [].
        newWords remove: each value ifAbsent: []]


The accessing messages keys and values can be used to obtain collections of the words in the opposites dictionary. Assuming the evaluation of all previous example expressions, then

opposites keys


returns the Set whose elements are

push come front stop cold


and

opposites values


returns the Bag whose elements are

pull go back start hot


Class SequenceableCollection

Class SequenceableCollection represents collections whose elements are ordered and are externally named by integer indices. SequenceableCollection is a subclass of Collection and provides the protocol for accessing, copying, and enumerating elements of a collection when it is known that there is an ordering associated with the elements. Since the elements are ordered, there is a well-defined first and last element of the collection. It is possible to ask the index of a particular element (indexOf:) and the index of the beginning of a sequence of elements within the collection (indexOfSubCollection:startingAt:). All collections inherit messages from class Object for accessing indexed variables. As described in Chapter 6, these are at:, at:put:, and size. In addition, SequenceableCollections support putting an object at all positions named by the elements of a Collection (atAll:put:), and putting an object at all positions in the sequence (atAllPut:). Sequences of elements within the collection can be replaced by the elements of another collection (replaceFrom:to:with: and replaceFrom:to:with:startingAt:).

accessing
atAll: aCollection put: anObject Associate each element of the argument, aCollection (an Integer or other external key), with the second argument, anObject.
atAllPut: anObject Put the argument, anObject, as every one of the receiver's elements.
first AAnswer the first element of the receiver. Report an error if the receiver contains no elements.
last Answer the last element of the receiver. Report an error if the receiver contains no elements.
indexOf: anElement AAnswer the first index of the argument, anElement, within the receiver. If the receiver does not contain anElement, answer 0.
indexOf: anElement ifAbsent: exception Block Answer the first index of the argument, anElement, within the receiver. If the receiver does not contain anElement, answer the result of evaluating the argument, exceptionBlock.
indexOfSubCollection: aSubCollection startingAt: anIndex If the elements of the argument, aSubCollection, appear, in order, in the receiver, then answer the index of the first element of the first such occurrence. If no such match is found, answer 0.
indexOfSubCollection: aSubCollection startingAt: anIndex ifAbsetnt: exceptionBlock Answer the index of the receiver's first element, such that that element equals the first element of the argument, aSubCollection, and the next elements equal the rest of the elements of aSubCollection. Begin the search of the receiver at the element whose index is the argument, anIndex. If no such match is found, answer the result of evaluating the argument, exceptionBlock.
replaceFrom: start to: stop with: replacementCollection Associate each index between start and stop with the elements of the argument, replacementCollection. Answer the receiver. The number of elements in replacementCollection must equal stop-start + 1.
replaceFrom: start to: stop with: replacementCollection startingAt: repStart Associate each index between start and stop with the elements of the argument, replacementCollection, starting at the element of replacementCollection whose index is repStart. Answer the receiver. No range checks are performed, except if the receiver is the same as replacementCollection but repStart is not 1, then an error reporting that indices are out of range will occur.
SequenceableCollection instance protocol


Examples of using these accessing messages, using instances of String, are

expression result
'aaaaaaaaaa' size 10
'aaaaaaaaaa' atAll: (2 to: 10 by: 2) put: $b 'ababababab'
'aaaaaaaaaa' atAllPut: $b 'bbbbbbbbbb'
'This string' first $T
'This string' last $g
'ABCDEFGHIJKLMNOP' indexOf: $F 6
'ABCDEFGHIJKLMNOP' indexof: $M ifAbsent: [0] 13
'ABCDEFGHIJKLMNOP' indexOf: $Z ifAbsent: [0] 0
'The cow jumped' indexOfSubCollection: 'cow' startingAt: 1 5
'The cow jumped' replaceFrom: 5 to: 7 with: 'dog' 'The dog jumped'
'The cow jumped' replaceFrom: 5 to: 7 with: 'the spoon ran' startingAt: 5 'The spo jumped'


Any of these examples could be similarly carried out with an instance of any subclass of SequenceableCollection, for example, with an Array. For the Array, @(The brown jug), replacement of brown by black is carried out by evaluating the expression

#(The brown jug) replaceFrom: 2 to: 2 with: #(black)


Notice that the last argument must be an Array as well. And notice that the replacement messages do not change the size of the original collection (the receiver), although they do alter the collection. It may be preferrable to preserve the original by creating a copy. The copying protocol of SequenceableCollections supports copying a sequence of elements in the collection, copying the entire collection with part of it replaced, copying the entire collection with an element deleted, or copying the entire collection with one or more elements concatenated.

copying
, aSequenceableCollection This is the concatenation operation. Answer a copy of the receiver with each element of the argument, aSequenceableCollection, added, in order.
copyFrom: start to: stop Answer a copy of a subset of the receiver, starting from element at index start until element at index stop.
copyReplaceAll: oldSubCollection with: newSubCollection Answer a copy of the receiver in which all occurrences of oldSubCollection have been replaced by newSubCollection.
copyReplaceFrom: start to: stop with: replacementCollection Answer a copy of the receiver satisfying the following conditions: If stop is less than start, then this is an insertion; stop should be exactly start-1, start = 1 means insert before the first character, start = size + 1 means append after last character. Otherwise, this is a replacement; start and stop have to be within the receiver's bounds.
copyWith: newElement Answer a copy of the receiver that is 1 bigger than the receiver and has newElement as the last element.
copyWithout: oldElement Answer a copy of the receiver in which all occurrences of oldElement have been left out.
SequenceableCollection instance protocol


Using the replace and copy messages, a simple text editor can be devised. The Smalltalk-80 system includes class String as well as-class Text, the latter providing support for associating the characters in the String with font or emphasis changes in order to mix character fonts, bold, italic, and underline. The message protocol for Text is that of a SequenceableCollection with additional protocol for setting the emphasis codes. For illustration purposes, we use an instance of class String, but remind the reader of the analogous application of editing messages for an instance of class Text. Assume that line is initially an empty string

line  String new: 0


Then

expression result
line ← line copyReplaceFrom: 1 to: 0 with: 'this is the first line tril' 'this is the first line tril'
line ← line copyReplaceAll: 'tril' with: 'trial' 'this is the first line trial'
line ← line copyReplaceFrom: (line size + 1) to: (line size) with: 'and so on' this is the first line trial and so on'
line indexOfSubCollection: 'trial' startingAt: 1 24
line ← line copyReplaceFrom: 29 to: 28 with: ' ' 'this is the first line trial and so on'


The last two messages of the copying protocol given above are useful in obtaining copies of an Array with or without an element. For example

expression result
#(one two three) copyWith: #four (one two three four)
#(one two three) copyWithout: #two (one three)


Because the elements of a SequenceableCollection are ordered, enumeration is in order, starting with the first element and taking each successive element until the last. Reverse enumeration is also possible, using the message reverseDo: aBlock. Enumeration of two SequenceableCollections can be done together so that pairs of elements, one from each collection, can be used in evaluating a block.

enumerating
findFirst: aBlock Evaluate aBlock with each of the receiver's elements as the argument. Answer the index of the first element for which the argument, aBlock evaluates to true.
findLast: aBlock Evaluate aBlock with each of the receiver's elements as the argument. Answer the index of the last element for which the argument, aBlock evaluates to true.
reverseDo: aBlock Evaluate aBlock with each of the receiver's elements as the argument, starting with the last element and taking each in sequence up to the first. For SequenceableCollections, this is the reverse of the enumeration for do:. aBlock is a one-argument block.
with: aSequenceableCollection do: aBlock Evaluate aBlock with each of the receiver's elements along with the corresponding element from aSequenceableCollection. aSequenceableCollection must be the same size as the receiver, and aBlock must be a two-argument block.
SequenceableCollection instance protocol


The following expressions create the Dictionary, opposites, which was introduced in an earlier example.

opposites  Dictionary new.
#(come cold front hot push stop)
    with: #(go hot back cold pull start)
    do: [ :key :value | opposites at: key put: value]


The Dictionary now has six associations as its elements.


Any SequenceableCollection can be converted to an Array or a MappedCollection. The messages are asArray and mappedBy: aSequenceableCollection.


SubClasses of SequenceableCollection

Subclasses of SequenceableCollection are OrderedCollection, LinkedList, Interval, and MappedCollection. ArrayedCollection is a subclass representing a collection of elements with a fixed range of integers as external keys. Subclasses of ArrayedCollection are, for example, Array and String.

Class OrderedCollection

OrderedCollections are ordered by the sequence in which objects are added and removed from them. The elements are accessible by external keys that are indices. The accessing, adding, and removing protocols are augmented to refer to the first and last elements, and to elements preceding or succeeding other elements.

OrderedCollections can act as stacks or queues. A stack is a sequential list for which all additions and deletions are made at one end of the list (called either the "rear" or the "front") of the list. It is often called a last-in first-out queue.

usual vocabulary OrderedCollection message
push newObject addLast: newObject
pop removeLast
top last
empty isEmpty


A queue is a sequential list for which all additions are made at one end of the list (the "rear"), but all deletions are made from the other end (the "front"). It is often called a first-in first-out queue.

usual vocabulary OrderedCollection message
add newObject addLast: newObject
delete removeFirst
front first
empty isEmpty


The message add: to an OrderedCollection means "add the element as the last member of the collection" and remove: means "remove the argument as an element." The message protocol for OrderedCollections, in addition to that inherited from classes Collection and SequenceableCollection, follows.

accessing
after: oldObject Answer the element after oldObject in the receiver. If the receiver does not contain oldObject or if the receiver contains no elements after oldObject, report an error.
before: oldObject Answer the element before oldObject in the receiver. If the receiver does not contain oldObject or if the receiver contains no elements before oldObject, report an error.
adding
add: newObject after: oldObject Add the argument, newObject, as an element of the receiver. Put it in the sequence just succeeding oldObject. Answer newObject. If oldObject is not found, then report an error.
add: newObject before: oldObject Add the argument, newObject, as an element of the receiver. Put it in the sequence just preceding oldObject. Answer newObject. If oldObject is not found, then report an error.
addAllFirst: anOrderedCollection Add each element of the argument, anOrderedCollection, at the beginning of the receiver. Answer anOrderedCollection.
addAllLast: anOrderedCollection Add each element of the argument, anOrderedCollection, to the end of the receiver. Answer anOrderedCollection.
addFirst: newObject Add the argument, newObject, to the beginning of the receiver. Answer newObject.
addLast: newObject Add the argument, newObject, to the end of the receiver. Answer newObject.
removing
removeFirst Remove the first element of the receiver and answer it. If the receiver is empty, report an error.
removeLast Remove the last element of the receiver and answer it. If the receiver is empty, report an error.
OrderedCollection instance protocol


Class SortedCollection

Class SortedCollection is a subclass of OrderedCollection. The elements in a SortedCollection are ordered by a function of two elements. The function is represented by a two-argument block called the sort block. It is possible to add an element only with the message add:; messages such as addLast: that allow the programmer to specify the order of inserting are disallowed for SortedCollections.


An instance of class SortedCollection can be created by sending SortedCollection the message sortBlock:. The argument to this message is a block with two-arguments, for example,

SortedCollection sortBlock: [ :a :b | a < = b ]


This particular block is the default sorting function when an instance is created simply by sending SortedCollection the message new. Thus examples of the four ways to create a SortedCollection are

SortedCollection new
SortedCollection sortBlock: [ :a :b | a > b ]
anyCollection asSortedCollection
anyCollection asSortedCollection: [ :a :b | a > b ]


It is possible to determine the block and to reset the block using two additional accessing messages to instances of SortedCollection. When the block is changed, the elements of the collection are, of course, re-sorted. Notice that the same message is sent to the class itself (sortBlock:) to create an instance with a particular sorting criterion, and to an instance to change its sorting criterion.

instance creation
sortBlock: aBlock Answer an instance of SortedCollection such that its elements will be sorted according to the criterion specified in the argument, aBlock.
SortedCollection instance protocol


accessing
sortBlock Answer the block that is the criterion for sorting elements of the receiver.
sortBlock: aBlock Make the argument, aBlock, be the criterion for ordering elements of the receiver.
SortedCollection class protocol


Suppose we with to maintain an alphabetical list of the names of children in a classroom.

children  s=SortedCollection new


The initial sorting criterion is the default block [ :a :b | a < = b]. The elements of the collection can be Strings or Symbols because, as we shall show presently, these kinds of objects respond to the comparison messages <, >, < =, and > =.

expression result
children add: #Joe Joe
children add: #Bill Bill
children add: #Alice Alice
children SortedCollection(Alice Bill Joe)
children add: #Sam Sam
a < b ] SortedCollection(Sam Joe Bill Alice)
children add: #Henrietta Henrietta
children SortedCollection(Sam Joe Henrietta Bill Alice)


The sixth message in the example reversed the order in which elements are stored in the collection, children.


Class LinkedList

LinkedList is another subclass of SequenceableCollection whose elements are explicitly ordered by the sequence in which objects are added and removed from them. Like OrderedCollection, the elements of a LinkedList can be referred to by external keys that are indices. Unlike OrderedCollection, where the elements may be any object, the elements of a LinkedList are homogeneous; each must be an instance of class Link or of a subclass of Link.


A Link is a record of a reference to another Link. Its message protocol consists of three messages. The same message (nextLink:) is used to create an instance of Link with a particular reference, and to change the reference of an instance.

instance creation
nextLink: aLink Create an instance of Link that references the argument, aLink.
LinkedList class protocol


accessing
nextLink Answer the receiver's reference.
nextLink: aLink Set the receiver's reference to be the argument, aLink.
LinkedList instance protocol


Since class Link does not provide a way to record a reference to the actual element of the collection, it is treated as an abstract class. That is, instances of it are not created. Rather, subclasses are defined that provide the mechanisms for storing one or more elements, and instances of the subclasses are created.


Since LinkedList is a subclass of SequenceableCollection, its instances can respond to the accessing, adding, removing, and enumerating messages defined for all collections. Additional protocol for LinkedList consists of

adding
addFirst: aLink Add aLink to the beginning of the receiver's list. Answer aLink.
addLast: aLink Add aLink to the end of the receiver's list. Answer aLink.
removing
removeFirst Remove the receiver's first element and answer it. If the receiver is empty, report an error.
removeLast Remove the receiver's last element and answer it. If the receiver is empty, report an error.
LinkedList instance protocol


An example of a subclass of Link in the Smalltalk-80 system is class Process. Class Semaphore is a subclass of LinkedList. These two classes are discussed in Chapter 15, which is about multiple independent processes in the system.


The following is an example of the use of LinkedList. Link does not provide instance information other than a reference to another Link. So, as an example, assume that there is a subclass of Link named Entry. Entry adds the ability to store one object. The instance creation message for an Entry is for: anObject, and its accessing message is element.

class name Entry
superclass Link
instance variable names element
class methods
instance creation
    for: anObject
        self new setElement: anObject
instance methods
accessing
    element
        element

printing
    printOn: aStream
        aStream nextPutAll: 'Entry for:', element printString

private
    setElement: anObject
        element  anObject


The classes LinkedList and Entry can then be used as follows.

expression result
link ← LinkedList new LinkedList ()
list add: (Entry for: 2) Entry for: 2
list add: (Entry for: 4) Entry for: 4
list addLast: (Entry for: 5) Entry for: 5
list addFirst: (Entry for: 1) Entry for: 1
list LinkedList (Entry for: 1 Entry for: 2 Entry for: 4 Entry for: 5)
list isEmpty false
list size 4
(each element) + value ] 12
list last Entry for: 5
list first Entry for: 1
list remove: (Entry for:4) Entry for: 4
list removeFirst Entry for: 1
list removeLast Entry for: 5
list first = = list last true


Class Interval

Another kind of SequenceableCollection is a collection of numbers representing a mathematical progression. For example, the collection might consist of all the integers in the interval from 1 to 100; or it might consist of all even integers in the interval from 1 to 100. Or the collection might consist of a series of numbers where each additional number in the series is computed from the previous one by multiplying it by 2. The series might start with 1 and end with the last number that is less than or equal to 100. This would be the sequence 1, 2, 4, 8, 16, 32, 64.


A mathematical progression is characterized by a first number, a limit (maximum or minimum) for the last computed number, and a method for computing each succeeding number. The limit could be positive or negative infinity. An arithmetic progression is one in which the computation method is simply the addition of an increment. For example, it could be a series of numbers where each additional number in the series is computed from the previous one by adding a negative 20. The series might start with 100 and end with the last number that is greater than or equal to 1. This would be the sequence 100, 80, 60, 40, 20.


In the Smalltalk-80 system, the class of collections called Intervals consists of finite arithmetic progressions. In addition to those messages inherited from its superclasses SequenceableCollection and Collection, class Interval supports messages for initialization and for accessing those values that characterize the instance. New elements cannot be added or removed from an Interval.


The class protocol of Interval consists of the following messages for creating instances.

instance creation
from: startInteger to: stopInteger Answer an instance of class Interval, starting with the number startInteger, ending with the number stopInteger, and using the increment 1 to compute each successive element.
from: startInteger to: stopInteger by: stepInteger Answer an instance of Interval, starting with the number startInteger, ending with the number stopInteger, and using the increment stepInteger to compute each successive element.
Interval class protocol


All messages appropriate to SequenceableCollections can be sent to an Interval. In addition, the instance protocol of Interval provides a message for accessing the increment of the arithmetic progression (increment).


Class Number supports two messages that provide a shorthand for expressing new Intervals--to: stop and to" stop by: step. Thus to create an Interval of all integers from 1 to 10, evaluate either

Interval from: 1 to: 10


or

1 to: 10


To create an Interval starting with 100 and ending with 1, adding a negative 20 each time, evaluate either

Interval from: 100 to: 1 by: -20


or

100 to: 1 by: -20


This is the sequence 100, 80, 60, 40, 20. The Interval need not consist of Integers--to create an Interval between 10 and 40, incrementing by 0.2, evaluate either

Interval from: 10 to: 40 by: 0.2


or

10 to: 40 by: 0.2


This is the sequence 10, 10.2, 10.4, 10.6, 10.8, 11.0.... and so on.


Note that we could provide the more general case of a progression by replacing the numeric value of step by a block. When a new element is to be computed, it would be done by sending the current value as the argument of the message value: to the block. The computations of size and do: would have to take this method of computation into account. The message do: to an Interval provides the function of the usual for-loop in a programming language. The Algol statement

for i := 10 step 6 until 100 do
    begin
    <statements>
    end


is represented by

(10 to: 100 by: 6) do: [ :i | statements ]


Numbers respond to the message to:by:do: as though the expression had been written as given in the example. So that iteration can be written without parentheses as

10 to: 100 by: 6 do: [ :i | statements ]


To increment by 1 every sixth numeric element of an OrderedCollection, numbers, evaluate

6 to: numbers size
    by: 6
    do: [ :index | numbers at: index put: (numbers at: index) + 1 ]


The Interval created is 6, 12, 18.... , up to the index of the last element of numbers. If the size of the collection is less than 6 (the supposedly first index), nothing happens. Otherwise elements at position 6, 12, 18, and so on, until the last possible position, are replaced.


Class ArrayedCollection

As stated earlier, class ArrayedCollection is a subclass of Collection. It represents a collection of elements with a fixed range of integers as external keys. ArrayedCollection has five subclasses in the Smalltalk-80 system--Array, String, Text, RunArray, and ByteArray.


An Array is a collection whose elements are any objects. It provides the concrete representation for storing a collection of elements that have integers as external keys. Several examples of the use of Arrays have already been given in this chapter.


A String is a collection whose elements are Characters. Many examples of the use of Strings have been given in this and in previous chapters. Class String provides additional protocol for initializing and comparing its instances.


Text represents a String that has font and emphasis changes. It is used in storing information needed for creating textual documents in the Smalltalk-80 system. An instance of Text has two instance variables, the String and an instance of RunArray in which an encoding of the font and emphasis changes is stored.


Class RunArray provides a space-efficient storage of data that tends to be constant over long runs of the possible indices. It stores repeated elements singly and then associates with each single element a number that denotes the consecutive occurrences of the element. For example, suppose the Text representing the String 'He is a good boy.' is to be displayed with the word "boy" in bold, and further suppose that the code for the font is 1 and for its boldface version is 2. Then the RunArray for the Text that is associated with "He is a good boy." (a String of 17 Characters) consists of 1 associated with 13, 2 associated with 3, and 1 associated with 1. That is, the first 13 Characters are in font 1, the next three in font 2, and the last in font 1.


A ByteArray represents an ArrayedCollection whose elements are integers between 0 and 255. The implementation of a ByteArray stores two bytes to a 16-bit word; the class supports additional protocol for word and double-word access. ByteArrays are used in the Smalltalk-80 system for storing time in milliseconds.


Class String

As stated earlier, the class protocol for String adds messages for creating a copy of another String (fromString: aString) or for creating a String from the Characters in a Stream (readFrom: aStream). The main significance of this second message is that pairs of embedded quotes are read and stored as one element, the quote character. In addition, class String adds comparing protocol like that specified in class Magnitude. We introduced some of these messages earlier in the description of class SortedCollection.

comparing
< aString Answer whether the receiver collates before the argument, aString. The collation sequence is ASCII with case differences ignored.
< = aString Answer whether the receiver collates before the argument, aString, or is the same as aString. The collation sequence is ASCII with case differences ignored.
> aString Answer whether the receiver collates after the argument, aString. The collation sequence is ASCII with case differences ignored.
> = aString Answer whether the receiver collates after the argument, aString, or is the same as aString. The collation sequence is ASCII with case differences ignored.
match: aString Treat the receiver as a pattern that can contain characters # and *. Answer whether the argument, aString, matches the pattern in the receiver. Matching ignores upper/lower case differences. Where the receiver contains the character #, aString may contain any single character. Where the receiver contains *, aString may contain any sequence of characters, including no characters.
sameAs: aString Answer whether the receiver collates precisely with the argument, aString. The collation sequence is ASCII with case differences ignored.
String instance protocol


We have not as yet given examples of using the last two messages.

expression result
'first string' sameAs: 'first string' true
'First String' sameAs: 'first string' true
'First String' = 'first string' false
'#irst string' match: 'first string' true
'*string' match: 'any string' true
'*.st' match: 'filename.st' true
'first string' match: 'first *' false


Strings can be converted to all lowercase characters or all uppercase characters. They can also be converted to instances of class Symbol.

converting
asLowercase Answer a String made up from the receiver whose characters are all lowercase.
asUppercase Answer a String made up from the receiver whose characters are all uppercase.
asSymbol Answer the unique Symbol whose characters are the characters of the receiver.
String instance protocol


Therefore we have

expression result
'first string' asUppercase 'FIRST STRING'
'First String' asLowercase 'first string'
'First' asSymbol First


Class Symbol

Symbols are arrays of Characters that are guaranteed to be unique. Thus

'a string' asSymbol = = 'a string' asSymbol


answers true. Class Symbol provides two instance creation messages in its class protocol for this purpose.

instance creation
intern: aString Answer a unique Symbol whose characters are those of aString.
internCharacter: aCharacter Answer a unique Symbol of one character, the argument, aCharacter.
Symbol class protocol


In addition, Symbols can be expressed literally using the character # as a prefix to a sequence of Characters. For example, #dave is a Symbol of four Characters. Symbols print without this prefix notation.


Class MappedCollection

Class MappedCollection is a subclass of Collection. It represents an access mechanism for referencing a subcollection of a collection whose elements are named. This mapping can determine a reordering or filtering of the elements of the collection. The basic idea is that a MappedCollection refers to a domain and a map. The domain is a Collection that is to be accessed indirectly through the external keys stored in the map. The map is a Collection that associates a set of external keys with another set of external keys. This second set of keys must be external keys that can be used to access the elements of the domain. The domain and the map, therefore, must be instances of Dictionary or of a Subclass of SequenceableCollection.


Take, for example, the Dictionary of word Symbols, opposites, introduced earlier.

key value
come go
cold hot
front back
hot cold
push pull
stop start


Suppose we create another Dictionary of synonym Symbols for some of the keys of the entries in opposites and refer to it by the variable name alternates.

key value
cease stop
enter come
scalding hot
shove push


Then we can provide a MappedCollection by evaluating the expression

words  MappedCollection collection: opposites map: alternates


Through words, we can access the elements of opposites. For example, the value of the expression words at: #cease is start (i.e., the value of the key cease in alternatives is stop; the value of the key stop in opposites is start). We can determine which part of opposites is referenced by words by sending words the message contents.

words contents


The result is a Bag containing the symbols start go cold pull.


The message at:put: is an indirect way to change the domain collection. For example

expression result
words at: #scalding cold
words at: #cease start
words at: #cease put: #continue continue
opposites at: #stop continue


Summary of Conversions Among Collections

In the sections describing the various kinds of collections, we have indicated which collections can be converted to which other collections. In summary, any collection can be converted to a Bag, a Set, an OrderedCollection, or a SortedCollection. All collections except Bags and Sets can be converted to an Array or a MappedCollection. Strings and Symbols can be converted into one another; but no collection can be converted into an Interval or a LinkedList.


Notes