Smalltalk по-русски
Advertisement
7.0

Скалярные величины[]

7.diagramma

Система Смолток предоставляет несколько классов представляющих объекты которые измеряют что либо с линейным порядком. Примеры таких измеряемых количеств из реального мира это (1) временные отрезки такие как даты и время, (2) пространственные количества такие как расстояние и (3) численные значения такие как действительные и рациональные числа.

Класс Величина[]

Является ли одно число меньше другого? Следует ли одна дата после другой даты? Предшествует ли одно время другому времени? Следует ли буква после другой в алфавите? Данное расстояние такое же или меньше чем друго расстояние?

Общий протокол для ответа на данные вопросы предоставляется классом Величина. Величина предоставляет протокол для объектов которым нужно иметь возможность сравниваться с одномерными величинами. Подклассами класса Величина являются Дата, Время и Число. Классы Знак (элемент цепи) и Ключ поиска (ключ в ассоциации словаря) также реализованы как подклассы класса Величина. Знаки интересны как пример неизменных объектов системы и поэтому они введены в данной главе; Ключ поиска это менее интересный объект и его рассмотрение отложено до тех пор пока мы не станем изучать главу о наборах. Класса Расстояние нет в текущей версии системы Смолток.

Протокол экземпляров Величины
сравнение
< величина отвечает является ли получатель меньше аргумента.
<= величина отвечает является ли получатель меньше чем или равен аргументу.
> величина отвечает является ли получатель больше аргумента.
>= величина отвечает является ли получатель больше чем или равен аргументу.
между: мин и: макс отвечает лежит ли получатель в интервале между аргументами мин и макс, включая границы. Т.е. он вычисляет условие сам >= мин и сам <= макс.

Несмотря на то что Величина наследует от своего суперкласса, Объекта, сообщение = для сравнения на равенство двух объектов, каждый вид Величина должен переопределить это сообщение. Метод связанный с селектором = в классе Величина:

сам ответственность подкласса.

Если подкласс Величина не реализовал =, то при попытке послать сообщение экземпляру подкласса появится сообщение об ошибке что подкласс должен реализовать это сообщение, как указано в его суперклассе.

Экземпляры видов Величины также могут отвечать на сообщения которые определяют какой из двух объектов больше или меньше.

Протокол экземпляров Величины
проверки
мин: величина возвращает получателя или аргумент, то у чего меньше величина.
макс: величина возвращает получателя или аргумент, то у чего больше величина.

Заметьте что протокол для идентичности ==, ~= и ~~ наследуется от класса Объект. Используя целые числа в качестве примера вида Величина получаем

предложение результат
3 <= 4. истина
3 > 4. ложь
5 между: 2 и: 6. истина
5 между: 2 и: 4. ложь
34 мин: 45. 34
34 макс: 45. 45

Программист не может создавать экземпляры Величина, а только её подклассы. Это происходит из за того что Величина не реализует все сообщения которые объявляет, т.к. она реализует несколько своих сообщений при помощи предложения сам ответственность подкласса.

Класс Дата[]

Сейчас после рассмотрения основных сообщений протокола Величины, можно добавить дополнительный протокол для поддержки арифметики и специальных запросов о линейный величинах. Первое уточнение которое мы рассмотрим будет подкласс Дата.

Экземпляры Даты представляют день от начала Юлианского календаря. День существует в виде конкретного месяца и года. Класс Дата знает о некоторых очевидных вещах: (1) в неделе 7 дней, каждый день имеет имя и номер 1, 2, ..., 7; (2) в году 12 месяцев, каждый имеет имя и номер 1, 2, ..., 12; (3) в месяце 28, 29, 30 или 31 день; и (4) некоторые годы являются високосными.

Протокол предоставляемый для объекта Дата поддерживает запросы о Датах в общем и о конкретных Датах. И Дата и Время являются интересными примерами классов системы у которых есть специальные свойства доступные для самих классов, в отличии от их экземпляров. Это "протокол класса" определён в метаклассе класса. Давайте сначала рассмотрим протокол класса Дата который поддерживает общие запросы.

Протокол класса Дата
общие запросы
день недели: имя дня возвращает номер дня в неделе, 1, 2, ..., 7 переданного в качестве аргумента, имя дня.
имя дня: номер дня возвращет символ который представляет имя дня чей номер это аргумент, номер дня, где 1 - понедельник, 2 - вторник, и т.д.
номер месяца: имя месяца возвращает номер месяца в году, 1, 2, ..., 12 переданного в качестве аргумента, имя месяца.
имя месяца: номер месяца возвращает символ который представляет имя месяца с номером являющимся переданным аргументом, номер месяца, где 1 - январь, 2 - февраль, и т.д.
дней в месяце: имя месяца для года: число год возвращает количество дней в месяце чьё имя это имя месяца в году число года (год нужен для подсчёта дней в вискокосном году).
дней в году: число год возвращает число дней в году, число год.
високосный год: число год возвращает 1 если число год это високосный год; иначе возвращает 0.
текущие дана и время возвращает ряд чей первый элемент это текущая дата (экземпляр класса Дата представляющий текущую дату) а второй элемент это текущее время (экземпляр класса Время представляющий текущее время).

Поэтому мы можем посылать следующие сообщения.

предолжение результат
Дата дней в году: 1982. 365
Дата день недели: #Среда. 3
Дата имя месяца: 10. Октябрь
Дата високосный год: 1972. 1 (означает что год високосный)
Дата дней в месяце: #Февраль для года: 1972. 29
Дата дней в месяце: #Февраль для года: 1971. 28

Класс Дата знает сокращения для имён месяцев.

Есть четыре сообщения которые могут быть использованы для создания экземпляров класса Дата. Один широко используется в системе Смолток, особенно для создания даты файла, это Дата сегодня.

Протокол класса Дата
создание экземпляров
сегодня возвращает экземпляр Даты представляющей день когда послано сообщение.
из дней: количество дней возвращает экземпляр Даты которая является днём номер количество дней после или до 1 января 1901 года (в зависимости от знака аргумента).
новый день: день месяц: имя месяца год: число год возвращает экземпляр Даты являющейся днём номер день в месяце именуемом имя месяца в году число год.
новый день: количество дней год: число год возвращает экземпляр Даты которая является днём номер количество дней от начала года число год.

Четыре примера сообщений создания экземпляра:

предложение результат
Дата сегодня. 3 феврала 1982
Дата из дней: 200. 20 июля 1901
Дата новый день: 6 месяц: #фев год: 82. 6 февраля 1982
Дата новый день: 3 год: 82. 3 января 1982

Сообщения которые можно послать экземплярам Даты разделены на следующие категории: доступ, запросы, арифметика и печать. Категории доступ и запросы содержат

  • номер дня, номер месяца или год
  • количество секунд, дней или месяцев с момента другой даты
  • общее количество дней в месяце или годе даты
  • количество дней оставшихся в месяце или годе даты
  • первый день месяца даты
  • имя дня недели или месяца даты
  • дата некоторого дня недели предшествующего экземпляру

Протоколом класса Дата поддерживается простая арифметика.

Протокол экземпляров Даты
арифметика
добавить дни: количество дней возвращает Дату на количество дней позже чем получатель.
вычесть дни: количество дней возвращает Дату которая на количество дней раньше чем получатель.
вычесть дату: дата возвращает Целое которое представляет количество дней между получателем и аргументом, дата.

Данная арифметика полезна, например, чтобы вычислить дату возврата для книги из библиотеки или плату за просроченную книгу. Допустим дата возврата это экземпляр Даты указывающий день до которого книга должна быть возвращена в библиотеку. Тогда

Дата сегодня вычесть дату: дата возврата.

вычисляет количество дней за которые нужно заплатить пеню. Если книга взята сегодня на две недели, тогда

Дата сегодня добавить дни: 14.

это дата возврата книги. Если библиотека заканчивает роботать за 16 дней до Рождества, то дата последнего рабочего дня это

(Дата новый день: 25 месяц: #December год: 1982) вычесть дни: 16.

Алгоритм вычисляющий размер пени которую долже заплатить должник сначала сравнивает текущую дату с датой возврата и затем, если дата возврата уже прошла, вычисляет пеню как 10 центов умноженных на количество дней превышения над датой возврата.

   Дата сегодня < дата возврата
      истина: [ пеня0. ]
      ложь: [ пеня0.10 * (Дата сегодня вычесть дату: дата возврата). ].

Класс Время[]

Экземпляры класса Время представляют некоторую секунду в дне. День начинается в полночь. Время это подкласс Величины. Подобно классу Дата, Время может отвечать на общие сообщеня запросы которые определены в протоколе класса.

Протокол класса Время
общие запросы
значение часов в миллисекундах возвращает количество миллисекунд с момента когда когда часы были сброшены или перешли через ноль.
миллисекунд на выполнение: замеряемый блок возвращает количество миллисекунд потребовавшихся на выполнение аргумента, замеряемый блок.
timeWords answer the seconds (in Greenwich Mean Time) since Jan. 1, 1901. The nswer is a four-element ByteArray (ByteArray is described in Chapter 10). - в текущей версии Сквика отсутствует.
всего секунд возвращает общее количество секунд от 1 января 1901 года с поправкой на временную зону и переводом часов на летнее время.
текущие дата и время возвращает ряд чей первый элемент это текущая дата (экземпляра класса Дата который представляет текущую дату) и второй элемент это текущее время (экземпляр класса Время который представляет текущее время). Результат посылки этого сообщения Времени идентичен результату посылке его Дате.

Единственный не очевидный запрос это миллисекунд на выполнение: замеряемый блок. В примере

Время миллисекунд на выполнение: [ Дата сегодня. ].

результатом будет количество миллисекунд потребовавшихся системе для вычисления сегодняшней даты. Из за того что существуют некоторые накладные расходы при ответе на это сообщение, и из за того что разрешение часов влияет на результат, то осторожный программист должен определять машинно-зависимую неточность связанную с выбором подходящего аргумента для этого сообщения.

Новый экземпляр Времени можно создать послав Времени сообщение сейчас; соответствующий метод читает текущее время из часов системы. В качестве альтернативы экземпляр Времени можно создать послав сообщение из секунд: количество секунд, где количество секунд это число секунд от полуночи.

Протокол класса Время
создание экземпляров
текущее возвращает экземпляр Времени представляющий секунду посылки сообщения.
из секунд: количество секунд возвращает экземпляра Времени который является количеством секунд от полуночи.

Протокол доступа для экземпляров Времени предоставляет такую информацию как количество часов (часы), минут (минуты) и секунд (секунды) которые представляет экземпляр. Так же поддерживается арифметика.

Протокол экземпляров Времени
арифметика
добавить время: количество времени возвращает экземпляр Времени который на аргумент, количество времени, больше чем получатель.
вычесть время: количество времени возвращает экземпляр Времени который на аргумент, количество времени, меньше чем получатель.

В выше приведённых сообщениях аргумент (количество времени) может быть или Датой или Временем. Чтобы это было возможным система должна уметь переводить Дату и Время в общую единицу измерения, она их переводит в секунды. В случае времени, переведённая величина это количество секунд от полуночи, в случае Даты, переведённая величина это количество секунд между 1 января 1901 года и тем же временем в дне получателя. Для поддержки этих методов, экземпляры каждого класса отвечают на преобразующий метод как секунды.

Протокол экземпляров Времени
преобразование
как секунды возвращает количество секунд от полуночи которое представляет получатель.


Протокол экземпляров Даты
преобразование
как секунды возвращает число секунд между временем 1 января 1901 года и тем же временем в дне представляемом получателем.

Арифметика для Времени может быть использована способами аналогичными способам для Даты. Допустим количество времени потраченное человеком на работу над некоторым проектом должно заносится в журнал чтобы заказчик мог изменять почасовую оплату. Допустим человек начал работу в время начала и работал непрерывно в течении дня до данного момента; зазвонил телефон - заказчик хочет узнать сегодняшние расходы. В этот момент оплата составляет 5 долларов в час

(Время текущее вычесть время: время начала) часы * 5.

не учитывает долполнительные секунды или минуты. Если оплата любой части часа большей 30 минут должна быть как за полный час тогда, если

(Время текущее вычесть время: время начала) минуты > 30.

добавляются дополнительные 5 долларов.

Кто более продуктивен, работник который зкончил работу с записью в журнал во время а или работник с временем б? Ответ - первый работник если время а < времени б. Протокол сравнения наследуется от суперклассов Величина и Объект.

Допустим времена вычисляются в течении дня, например, при вычислении времени машины в четырёхдневном ралли. Если в первый день ралли началось во время начала дня день начала, тогда время прибытия машины на финишную линию вычисляется следующим образом. Допустим что время старта 6:00.

время началаВремя из секунд: 60 * 60 * 6.

2 февраля 1982 года.

дата стартаДата новый день: 2 месяц: #фев год: 82.

Время прошедшее да начала текущего дня это

   начало сегодня(((Время из секунд: 0) добавить время: Дата сегодня) вычесть время: дата старта)
      вычесть время: время начала.

Здесь добавляеются все секунды от начала 1 января 1901 года до начала сегодняшнего дня и затем вычитаются все секунды от начала 1 января 1901 года до начала даты старта. Это эквивалентно добавлению количество секунд в количестве дней гонги, но затем программист должен произвести все преобразования.

(Дата сегодня вычесть дату: дата старта) * 24 * 60 * 60.

Добавив текущее время мы получим время потраченное машиной на прохождение ралли.

начало сегодня добавить время: Время текущее.

Класс Знак[]

Класс Знак это третий подкласс класса Величина который мы рассмотрим. Это вид Величины т.к. экземпляры класса Знак принадлежат упорядоченной последовательности и им можно задавать вопрос предшествует ли данный знак (<) или следует после (>) другова знака в алфавите.

Знаки могут быть записаны при помощи литерала с помощью предшествующего знака доллара ($); так $А это Знак представляющий большую букву "А". Протокол для создания экземпляров Знака состоит из:

Протокол класса Знак
создание экземпляров
значение: целое возвращает экземпляр Знака чьё значение это аргумент, целое. Значение это code point уникод символа. Например, Знак значение: 16о1040 это большая буква "А".
значение цифры: целое возвращет экземпляр Знака который является цифрой с номером равным аргументу, целое. Например ответ будет $9 если аргумент это 9; ответ $0 если аргумент 0; ответ $A если аргумент 10, и ответ $Z если аргумент равен 35. Этот метод полезен при преобразвании чисел в цепи. Обычно используются буквы только до $F (для шестнадцатиричных чисел).

Протокол класса содержит набор сообщений для доступа к знакам которые сложно различать по внешнему виду: забой, пс (перевод строки), эскейп, новая страница, пробел и таб (табуляция).

Сообщения к экземплярам Знака поддерживают доступ к значению кода знака и проверку типа знака. Единственное состояние Знака это его значение которое никогда не может быть изменено. Объекты которые не изменяют своё состояние называются неизменными объектами. Это означает что будучи однажды созданы они не разрушаются и воссоздаются когда они снова становятся нужными. Когда создаётся новый Знак с кодом между 0 и 255 возвращается ссылка на уже существующий Знак. Поэтому 256 Знаков уникальны. Кроме Знаков система Смолток содержит Малые целые и Символы которые являются неизменными объектами.

Протокол экземпляров Знака
доступ
значение АСКОИ возвращает число соответствующее кодирове АСКОИ (американский стандартный код обмена информацией) для получателя.
значение цифры возвращает число соответствующее цифре системы счисления представляемой получателем (см. сообщения создания экземпляра значение цифры:).
проверки
это буква или цифра возвращает истину если получатель это буква или цифра.
это цифра отвечает является ли получатель цифрой.
это буква отвечает является ли получатель буквой.
в нижнем регистре отвечает является ли получатель буквой в нижнем регистре.
в верхнем регистре отвечает является ли получатель буквой в верхнем регистре.
это разделитель отвечает является ли получатель одним из знаков разделителей в синтаксисе предложений: пробел, пс, таб, перевод строки или перевод страницы.
это гласный отвечает является ли получатель одним из гласных: a, e, i, o или u в верхнем или нижнем регистре.

Протокол экземпляра также предоставляет протокол преобрабозвания букв в верхний или нижний регистр (как верхний регистр и как нижний регистр) и преобразования в символ (как символ). Простое сравнение по алфавиту показывает использование протокола сравнения для экземпляров Знака. Допустим мы хотим узнать прешествует ли одна цепь другой в телефонной книге. Цепи представляют сообщения от: для нахождения элемента с номером переданным в качестве аргумента; элементы Цепей это Знаки. Поэтому 'абв' от: 2 это $б. Ниже подразумевается что в классе Цепь определён метод мин:. Метод возвращает Цепь, получателя или сообщения или его аргумент, то что идёт первым в алфавитном порядке.

мин: цепь
   1
      до: сам размер
      делать: [
         :номер |
         номер > цепь размер истина: [ цепь. ].
         (сам от: номер) > (цепь от: номер) истина: [ цепь. ].
         (сам от: номер) < (цепь от: номер) истина: [ сам. ]. ].
   сам.

Алгоритм содержит два предложения. Первое это цикл по всем элементам получателя. Цикл заканчивается когда либо (1) аргумент, цепь, не имеет знака с которым можно сравнить следующий знак получателя (т.е. номер > цепь размер); (2) следующий знак получателя следует за знаком в цепи (т.е. (сам оте: номер) > (цепь от: номер)); или (3) следующий знак получател идёт после следующего знака цепи. Для примера случая (1) сравним 'абв' и 'абвг' это сравнение заканчивается когда номер = 4; ответ это 'абв' она идёт раньше в алфавитном порядке. Для пример случая (2) сравним 'абфд' и 'абвг'. Когда номер = 3 $ф > $в это истина; ответом будет 'абвг'. Пример случая (3), сравним 'ая' и 'бю' метод завершается когда номер = 1; ответ это 'ая'. В случае когда у получателя меньше знаков чем у аргумента, даже если получатель это начальная подцепь аргумента, выполнение первого предложения заканчивается; результат это получатель. Примером могут быть 'абв' и 'абвг'.

Заметьте что арифметика для знаков не поддерживается. Например следующее предложение неправильно.

а + 1.

Возникнет ошибка т.к. знаки не понимают сообщения +.

Advertisement