Smalltalk80LanguageImplementation:Chapter 07

From 흡혈양파의 번역工房
Jump to navigation Jump to search
Chapter 7 Linear Measures

Linear Measures

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


The Smalltalk-80 system provides several classes representing objects that measure something with linear ordering. Real world examples of such measurable quantities are (1) temporal quantities such as dates and time, (2) spatial quantities such as distance, and (3) numerical quantities such as reals and rationals.


Class Magnitude

IS one number less than another number? Does one date come after another date? Does one time precede another time? Does a character come after another one in the alphabet? Is one distance the same or less than another distance?


The common protocol for answering these queries is provided in the class Magnitude. Magnitude provides the protocol for objects that have the ability to be compared along a linear dimension. Subclasses of class Magnitude include Date, Time, and Number. Classes Character (an element of a string) and LookupKey (a key in a dictionary association) are also implemented as subclasses of class Magnitude. Character is interesting as an example of immutable objects in the system and so is introduced in this chapter; LookupKey is less interesting and is deferred until needed in the chapter on collections. A class Distance is not provided in the actual Smalltalk-80 system.

comparing
< aMagnitude Answer whether the receiver is less than the argument.
< = aMagnitude Answer whether the receiver is less than or equal to the argument.
> aMagnitude Answer whether the receiver is greater than the argument.
> = aMagnitude Answer whether the receiver is greater than or equal to the argument.
between: min and: max Answer whether the receiver is greater than or equal to the argument, rain, and less than or equal to the argument, max.
Magnitude instance protocol


Although Magnitude inherits from its superclass, Object, the message = for comparing the equality of two quantifiable objects, every kind of Magnitude must redefine this message. The method associated with = in class Magnitude is

self subclassResponsibility


If a subclass of Magnitude does not implement = , then an attempt to send the message to an instance of the subclass results in the special error message that a subclass should have implemented the message,as specified in its superclass.


An instance of a kind of Magnitude can also respond to messages that determine which of two objects that can be linearly measured is the larger or the smaller.

testing
min: aMagnitude Answer the receiver or the argument, whichever has the lesser magnitude.
max: aMagnitude Answer the receiver or the argument, whichever has the greater magnitude.
Magnitude instance protocol


Note that protocol for the equality comparisons = =, ~ = , and ~~ is inherited from class Object. Using Integers as the example kinds of Magnitudes, we have

expression result
3 < = 4 true
3 > 4 false
5 between: 2 and: 6 true
5 between: 2 and: 4 false
34 min: 45 34
34 max: 45 45


The programmer does not create instances of Magnitude, but only of its subclasses. This is due to the fact that Magnitude is not able to implement all of the messages it specifies, indeed, that it implements one or more of these messages by the expression self subclassResponsibility.


Class Date

Now that we have defined the general protocol of Magnitudes, it is possible to add additional protocol that supports arithmetic and inquiries about specific linear measurements. The first refinement we will examine is the subclass Date.


An instance of Date represents a specific day since the start of the Julian calendar. A day exists in a particular month and year. Class Date knows about some obvious information: (1) there are seven days in a week, each day having a symbolic name and an index 1, 2, ..., or 7; (2) there are 12 months in a year, each having a symbolic name and an index, 1, 2, ..., or 12; (3) months have 28, 29, 30, or 31 days; and (4) a particular year might be a leap year.


Protocol provided for the object, Date, supports inquiries about Dates in general as well as about a specific Date. Both Date and Time provide interesting examples of classes in the system for which special knowledge is attributed to and accessible from the class itself, rather than from its instances. This "class protocol" is specified in the metaclass of the class. Let's first look at the class protocol of Date supporting general inquiries.

general inquiries
dayOfWeek: dayName Answer the index in a week, 1, 2 . . . . or 7, of the day named as the argument, dayName.
nameOfDay: dayIndex Answer a Symbol that represents the name of the day whose index is the argument, dayIndex, where 1 is Monday, 2, is Tuesday, and so on.
indexOfMonth: monthName Answer the index in a year, 1, 2, ..., or 12, of the month named as the argument, monthName.
nameOfMonth: monthIndex Answer a Symbol that represents the name of the month whose index is the argument, monthIndex, where 1 is January, 2, is February, and so on.
daysInMonth: monthName forYear: yearInteger Answer the number of days in the month whose name is monthName in the year yearInteger (the year must be known in order to account for a leap year).
daysInYear: yearInteger Answer the number of days in the year, yearInteger.
leapYear: yearInteger Answer 1 if the year yearInteger is a leap year; answer 0 otherwise.
dateAndTimeNow Answer an Array whose first element is the current date (an instance of class Date representing today's date) and whose second element is the current time (an instance of class Time representing the time right now).
Date class protocol


Thus we can send the following messages.

expression result
Date daysInYear: 1982 365
Date dayOfWeek: #Wednesday 3
Date nameOfMonth: 10 October
Date leapYear: 1972 1 (meaning it is a leap year)
Date daysInMonth: #February forYear:1972 29
Date daysInMonth: #Feb forYear: 1972 28


Date is familar with the common abbreviations for names of months.


There are four messages that can be used to create an instance of class Date. The one commonly used in the Smalltalk-80 system, notably for marking the creation date of a file, is Date today.

instance creation
today Answer an instance of Date representing the day the message is sent.
fromDays: dayCount Answer an instance of Date that is dayCount number of days before or after January 1, 1901 (depending on the sign of the argument).
newDay: day month: monthName year: yearInteger Answer an instance of Date that is day number of days into the month named monthName in the year yearInteger.
newDay: dayCount year: yearInteger Answer an instance of Date that is dayCount number of days after the beginning of the year yearInteger.
Date instance protocol


Four examples of instance creation messages are

expression result
Date today 3 February 1982
date fromDays: 200 20 July 1901
date newDay: 6 month: #Feb year: 82 6 February 1982
Date newDay: 3 year: 82 3 January 1982


Messages that can be sent to an instance of Date are categorized as accessing, inquiries, arithmetic , and printing messages. Accessing and inquiries about a particular day consist of

  • the day index, month index, or year
  • the number of seconds, days, or months since some other date
  • the total days in the date's month or year
  • the days left in the date's month or year
  • the first day of the date's month
  • the name of the date's weekday or month
  • the date of a particular weekday previous to the instance


Simple arithmetic is supported in the protocol of class Date.

arithmetic
addDays: dayCount Answer a Date that is dayCount number of days after the receiver.
subtractDays: dayCount Answer a Date that is dayCount number of days before the receiver.
subtractDate: aDate Answer an Integer that represents the number of days between the receiver and the argument, aDate.
Date instance protocol


Such arithmetic is useful, for example, in order to compute due dates for books in a library or fines for late books. Suppose dueDate is an instance of Date denoting the day a book was supposed to be returned to the library. Then

Date today subtractDate: dueDate


computes the number of days for which the borrower should be fined. If a book is being borrowed today and it can be kept out for two weeks, then

Date today addDays: 14


is the due date for the book. If the librarian wants to quit work 16 days before Christmas day, then the date of the last day at work is

(Date newDay: 25 month: #December year: 1982) subtractDays: 16


An algorithm to determine the fine a borrower must pay might first compare today's date with the due date and then, if the due date has past, determine the fine as a 10-cent multiple of the number of days overdue.

Date today < dueDate
    ifTrue: [fine   0]
    ifFalse: [fine   0.10 * (Date today subtractDate: dueDate)]


Class Time

An instance of class Time represents a particular second in a day. Days start at midnight. Time is a subclass of Magnitude. Like class Date, Time can respond to general inquiry messages that are specified in the class protocol.

general inquiries
millisecondClockValue Answer the number of milliseconds since the millisecond clock was last reset or rolled over to 0
millisecondsToRun: timedBlock Answer the number of milliseconds timedBlock takes to return its value.
timeWords Answer the seconds (in Greenwich Mean Time) since Jan. 1, 1901. The answer is a four-element ByteArray (ByteArray is described in Chapter 10).
totalSeconds Answer the total seconds from Jan. 1, 1901, corrected for time zone and daylight savings time.
dateAndTimeNow Answer an Array whose first element is the current date (an instance of class Date that represents today's date) and whose second element is the current time (an instance of class Time that represents the time right now). The result of sending this message to Time is identical to the result of sending it to Date.
Time class protocol


The only non-obvious inquiry is millisecondsToRun: timedBlock. An example is

Time millisecondsToRun: [Date today]


where the result is the number of milliseconds it took the system to compute today's date. Because there is some overhead in responding to this message, and because the resolution of the clock affects the result, the careful programmer should determine the machine-dependent uncertainties associated with selecting reasonable arguments to this message.


A new instance of Time can be created by sending Time the message now; the corresponding method reads the current time from a system clock. Alternatively, an instance of Time can be created by sending the message fromSeconds: secondCount, where SecondCount is the number of seconds since midnight.

instance creation
now Answer an instance of Time representing the second the message is sent.
fromSeconds: secondCount Answer an instance of Time that is secondCount number of seconds since midnight.
Time class protocol


Accessing protocol for instances of class Time provide information as to the number of hours (hours), minutes (minutes) and seconds (seconds) that the instance represents.


Arithmetic is also supported.

arithmetic
addTime: timeAmount Answer an instance of Time that is the argument, timeAmount, after the receiver.
subtractTime: timeAmount Answer an instance of Time that is the argument, timeAmount, before the receiver.
Time instance protocol


In the messages given above, the arguments (timeAmount) may be either Dates or Times. For this to be possible, the system must be able to convert a Date and a Time to a common unit of measurement ; it converts them to seconds. In the case of Time, the conversion is to the number of seconds since midnight; in the case of Date, the conversion is to the number of seconds between a time on January 1, 1901, and the same time in the receiver's day. To support these methods, instances of each class respond to the conversion message asSeconds .

Time instance protocol
asSeconds Answer the number of seconds since midnight that the receiver represents.
Time instance protocol


converting
asSeconds Answer the number of seconds between a time on January 1, 1901, and the same time in the receiver's day.
Date instance protocol


Arithmetic for Time can be used in ways analogous to that for Date. Suppose the amount of time a person spends working on a particular project is to be logged so that a customer can be charged an hourly fee. Suppose the person started work at startTime and worked continuously during the day until right now; the phone rings and the customer wants to know today's charges. At that moment, the bill at $5.00 an hour is

(Time now subtractTime: startTime) hours * 5


ignoring any additional minutes or seconds. If a charge for any fraction of an hour over 30 minutes is to be charged as a full hour then an additional $5.00 is added if

(Time now subtractTime: startTime) minutes > 30


Who is more productive, the worker who finished the job with time logged at timeA or the worker with time timeB? The answer is the first worker if timeA < timeB. Comparing protocol is inherited from the superclasses Magnitude and Object.


Suppose times are computed across days, for example, in computing the time of a car in a four-day rally. If the first day of the rally started at startTime on day startDate, then the time for a car arriving at the finish line right now is computed as follows.


Let the start time be 6:00 a.m.

startTime  Time fromSeconds: (60*60,6).


on February 2, 1982

startDate   Date newDay: 2 month: #Feb year: 82.


The time that has passed up to the start of the current day is

todayStart   (((Time fromSeconds: 0) addTime: Date today)
    subtractTime: startDate)
        subtractTime: startTime


That is, add all the seconds since Jan. 1, 1901, up to the start of today and then subtract all the seconds since Jan. 1, 1901, up to the start of the start date. This is equivalent to adding the number of seconds in the number of elapsed days, but then the programmer would have to do all the conversions.

(Date today subtractDate: startDate), 24*60,60)


By adding the current time, we have the elapsed rally time for the car.

todayStart addTime: Time now


Class Character

Class Character is the third subclass of class Magnitude we shall examine. It is a kind of Magnitude because instances of class Character form an ordered sequence about which we can make inquiries such as whether one character precedes ( < ) or succeeds ( > ) another character alphabetically. There are 256 instances of class Character in the system. Each one is associated with a code in an extended ASCII character set.


Characters can be expressed literally by preceding the alphabetic character by a dollar sign ($); thus, $A is the Character representing the capital letter "A". Protocol for creating instances of class Character consists of

instance creation
value: anInteger Answer" an instance of Character whose value is the argument, anInteger. The value is associated with an element of the ASCII character set. For example, Character value: 65 is a capital "A".
digitValue: anInteger Answer an instance of Character whose digit value is the argument, anInteger. For example, answer $9 if the argument is 9; answer $0 for 0; answer $A for 10, and $Z for 35. This method is useful in parsing numbers into strings. Typically, only Characters up to $F are useful (for base 16 numbers).
Character class protocol


Class protocol, that is, the set of messages to the object Character, provides a vocabulary for accessing characters that are not easy to distinguish when printed: backspace, cr, esc, newPage (that is, form feed), space, and tab.


Messages to instances of Character support accessing the ASCII value and the digit value of the instance and testing the type of character. The only state of a Character is its value which can never change. Objects that can not change their internal state are called immutable objects. This means that, once created, they are not destroyed and then recreated when they are needed again. Rather, the 256 instances of Character are created at the time the system is initialized and remain in the system. Whenever a new Character whose code is between 0 and 255 is requested, a reference is provided to an already existing Character. In this way the 256 Characters are unique. Besides Characters, the Smalltalk-80 system includes SmallIntegers and Symbols as immutable objects.

accessing
asciiValue Answer the number corresponding to the ASCII encoding for the receiver.
digitValue Answer the number corresponding to the numerical radix represented by the receiver (see the instance creation message digitValue: for the correspondences).
testing
isAlphaNumeric Answer true if the receiver is a letter or a digit.
isDigit Answer whether the receiver is a digit.
isLetter Answer whether the receiver is a letter.
isLowercase Answer whether the receiver is a lowercase letter.
isUppercase Answer whether the receiver is an uppercase letter.
isSeparator Answer whether the receiver is one of the separator characters in the expression syntax: space, cr, tab, line feed, or form feed.
isVowel Answer whether the receiver is one of the vowels: a, e, i, o, or u, in upper or lowercase.
Character instance protocol


Instance protocol also provides conversion of a character into upper or lowercase ( asLowercase and asUppercase) and into a symbol (asSymbol).


A simple alphabetic comparison demonstrates the use of comparing protocol for instances of Character. Suppose we wish to know if one string of characters precedes another string in the telephone book. Strings respond to the message at: to retrieve the element whose index is the argument; elements of Strings are Characters. Thus 'abc' at: 2 is $b. In the following we assume we are specifying a method in class String whose message selector is min:. The method returns a String, either the receiver of the message rain: or its argument, whichever is collated first alphabetically.

min: aString
    1 to: self size do:
        [ :index |
            (index > aString size) ifTrue: [ aString].
            (self at: index) > (aString at: index) ifTrue: [ aString].
            (self at: index) < (aString at: index) ifTrue: [ self]].
     self


The algorithm consists of two statements. The first is an iteration over each element of the receiver. The iteration stops when either (1) the argument, aString, no longer has a character with which to compare the next character in the receiver (i.e., index > aString size); (2) the next character in self comes after the next character in aString (i.e., (self at: index) > (aString at: index)); or (3) the next character in self comes before the next character in aString. As an example of (1), take the comparison of 'abcd' and 'abc' which terminates when index = 4; the answer is that 'abc' is first alphabetically. For (2), suppose we compare 'abde' with 'abce'. When index = 3, $d > $c is true; the answer is 'abce'. For (3), compare 'az' with 'by' which terminates when index -- 1; the answer is 'az'. In the case that the receiver has fewer characters than the argument, even when the receiver is the initial substring of the argument, the first statement will complete and the second statement is evaluated; the result is the receiver. An example is the comparison of 'abc' and 'abcd'.


Note that arithmetic on characters is not supported. For example, the following expression is incorrect.

a   $A + 1


The error occurs because a Character does not understand the message +.


Notes