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

Протокол потоков[]

12.1

Классы наборов предоставляют основные структуры данных для хранения объектов в виде линейных и нелинейных групп. Протокол этих классов позволяет напрямую обращаться (сохранять и получать) к отдельным элементам. Также, через перечисляющие сообщения, поддерживается непрерывный доступ ко всем объектам, по порядку. Однако, они не поддерживают смешивания двух видов операций доступа - перечисление и запоминание. Также протокол наборов поддерживает доступ к индивидуальным элементам, к одному за раз, потому что внешняя позиция хранится отдельно.

Если не существует легко вычисляемых внешних имён для каждого элемента, произвольное перечисление элементов нельзя произвести эффективно. Например, возможно последовательное чтение элементов Упорядоченного набора используя комбинацию первый и после:, до тех пор пока элементы набора уникальны. Альтернативный подход использует запоминание самим набором к которому элементу было последние обращение. Это называется "ссылка на положение" в последующем обсуждении. Однако, возможность раздельного доступа к последовательности элементов означает что нужно хранить отдельно последний использованный элемент.

Класс Поток предоставляет возможность сохранять ссылку на положение в наборе объектов. Фраза поток через набор означает доступ к элементам набора таким образом что возможно перечисление или запоминание каждого элемента, одного за раз, с возможным перемешиванием этих операций. Создавая несколько Потоков для одного набора, можно управлять многими ссылками на положение в одном и том же наборе.

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

Класс Поток[]

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

Протокол экземпляров Потока
доступ-чтение
следующий Возвращает следующий объект доступный получателю.
следующий: целое Возвращает следующие целое количество объектов доступных получателю. Как правило, ответом будет набор того же класса что и набор получателя.
следующий совпадает с: объект Получает следующий объект и отвечате равен ли он аргументу, объект.
содержимое Возвращает все объекты в наборе доступном получателю. Как правило, ответом будет набор того же класса что и набор получателя.

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

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

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

Протокол экземпляров Потока
проверки
в конце Отвечает не может ли получатель вернуть объекты из набора.

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

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

Реализация этого перечисляющего сообщения зависит от сообщений в конце и следующий. Покажем этот метод как пример использования этих сообщений.

делать: блок
   [ сам в конце. ] пока ложь: [ блок значение: сам следующий. ].

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

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

Bob Dave Earl Frank Harold Jim Kim Mike Peter Rick Sam Tom

и ссылка такая что следующий доступный элемент это Bob. Тогда:

предложение результат
доступ следующий. Bob
доступ следующий. Dave
доступ следующий совпадает с: #Bob. ложь
доступ следующий совпадает с: #Frank. истина
доступ следующий. Harold
доступ пом следующим: #James. James
доступ содержимое. (Bob Dave Earl Frank

Harold James Kim Mike Peter Rick Sam Tom)

доступ пом следующим все: #( #Karl #Larry #Paul ). (Karl Larry Paul)
доступ содержимое. (Bob Dave Earl Frank

Harold James Karl Larry Paul Rick Sam Tom)

доступ следующими: 2 пом: #John. John
доступ содержимое. (Bob Dave Earl Frank

Harold James Karl Larry Paul John John Tom)

доступ следующий. Tom
доступ в конце. истина

Позиционируемый поток[]

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

Класс Позиционируемый поток это подкласс класса Поток. Он предоставляет дополнительный протокол подходящий для потоков которые могут изменять свою ссылку положение, но, это абстрактный класс т.к. он не предоставляет реализации унаследованных сообщений следующий и следующим пом: объект. Реализация этих сообщений оставлена подклассам Позиционируемого потока - Потоку чтения, Потоку записи и Потоку чтения записи.

Позиционируемый поток создаётся при помощи посылки классу одного из двух сообщений создания экземпляра, на: набор или на: набор от: первый номер до: последний номер. Аргумент, набор, это набор доступный потоку; во втором случае доступна копия поднабора, т.е. поднабор ограниченный двумя аргументами первый номер и последний номер.

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

Позиционируемый поток поддерживыает дополнительный протокол для доступа и проверки содержимого набора.

Протокол экземпляров Позиционируемого потока
проверки
пустой Отвечает истина если набор доступный получателю не имеет элементов, иначе возвращает ложь.
доступ
считать Возвращает следующий элемент из набора (как и в сообщении следующий), но не изменяет ссылку на положение. Возвращает пусто если находится в конце.
считать для: объект Определяет ответ на сообщение считать. Если это то же что и аргумент, объект, то увеличивает ссылку на положение и возвращает истину. Иначе возвращает ложь и не изменяет ссылку на положение.
вплоть до: объект Возвращет набор элементов начинающийся со следующего элемента доступного получателю, и до, но не включая, следующего элемента который равен объекту. Если объекта нету в наборе, то возвращается весь остаток набора.

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

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

Класс Поток чтения[]

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

   доступПоток чтения
      на: #(
         #Bob
         #Dave
         #Earl
         #Frank
         #Harold
         #Jim
         #Kim
         #Mike
         #Peter
         #Rick
         #Sam
         #Tom ).


предложение результат
доступ следующий. Bob
доступ следующий совпадает с: #Dave. истина
доступ считать. Earl
доступ следующий. Earl
доступ считать для: #Frank. истина
доступ следующий. Harold
доступ вплоть до: #Rick. (Jim Kim Mike Peter)
доступ позиция. 10
доступ пропустить: 1. сам доступ
доступ следующий. Tom
доступ в конце. истина
доступ сбросить. сам доступ
доступ пропустить до: #Frank. истина
доступ следующий. Harold

Класс Поток записи[]

Класс Поток записи это подкласс Позиционируемого потока предоставляющего доступ для записи элементов в набор. Ни одно из сообщений следующий, следующий: и делать: нельзя успешно послать Потоку записи.

Поток записи используется во всей системе Смолток как часть методов печати или помещения строки описывающей любой объект. Каждый объект системы может отвечать на сообщения печатать в: поток и поместить в: поток. Методы связанные с этими сообщениями содержат последовательность сообщений аргументу, который является видом Потока позволяющим записывать элементы в доступный ему набор. Эти сообщения это пом следующим:, с аргументом Знаком; и пом следующими все:, с аргументом Цепью или Символом. Проиллюстрируем эту идею примером.

Как описано в шестой главе, протокол печати класса Объект включает сообщение цепь для печати. Реализация этого сообщения:

цепь для печати
   | поток |
   потокПоток записи на: (Цепь новый: 16).
   сам печатать в: поток.
   поток содержимое.

Если набору посылается сообщение цепь для печати, то ответом будет Цепь которая описывает экземпляр. Метод создаёт Поток записи в который будет помещаться набор, набору посылается сообщение печатать в:, и затем возвращается содержимое результирующего Потока записи. Сообщение цепь для помещения реализовано для всех объектов в классе Объект подобным образом, различие заключается в том что во втором предложении используется сообщение поместить в: поток вместо сообщения печатать в: поток.

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

Множество (один два три четыре)

Упорядоченный набор с теми же элемента напечатается в поток так:

Упорядоченный набор (один два три четыре)

и т.д.

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

Вот реализации сообщения печатать в: класса Набор.

печатать в: поток
   поток пом следующими все: сам класс имя.
   поток пробел.
   поток пом следующим: $(.
   сам делать: [ :элемент | элемент печатать в: поток. поток пробел. ].
   поток пом следующим: $).

Обратите внимание на сообщение пробел посылаемое Потоку записи (поток). Это и несколько других сообщений предоставляются классом Поток записи для поддержки кратких выражений помещающих разделители в Потоки. В эти сообщения включаются:

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

Поэтому чтобы составить Цепь:

name	city
bob	New York
joe	Chicago
bill	Rochester

из двух соответствующих Рядов,

имена#( #bob #joe #bill ).
города#( 'New York' 'Chicago' 'Rochester' ).

выполните предложения:

   потокПоток записи на: (Цепь новый: 16).
   поток пом следующими все: 'name'.
   поток таб.
   поток пом следующими все: 'city'.
   поток пс; пс.
   имена
      с: города
      делать: [
         :имя :город |
         поток пом следующими все: имя.
         поток таб.
         поток пом следующими все: город.
         поток пс. ].

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

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

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

Поэтому если Цепь с именем заголовок уже существует с содержит:

name	city

тогда предыдущий пример нужно строить так:

   потокПоток записи с: заголовок.
   имена
      с: города
      делать: [
         :имя :город |
         поток пом следующими все: имя.
         поток таб.
         поток пом следующими все: город.
         поток пс. ].
   поток содержимое.

Класс Поток записи чтения[]

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

Потоки генерируемых элементов[]

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

Класс Случайное число, введённый в восьмой главе, это подкласс Потока который находит свои элементы на основе алгоритма испльзующего число в качестве затравки. Случайное число предоставляет конкретную реализацию для сообщений следующий и в конце. Т.к. размер набора бесконечен, то поток никогда не заканчивается; кроме того, Случайное число не может отвечать на сообщение содержимое. Оно может отвечать на сообщения делать:, но метод никогда не закончит своё выполнение без специального вмешательства программиста.

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

имя класса Случайное число
суперкласс Поток
имена переменных экземпляра затравка
методы класса
создание экземпляров
новый
   сам основной новый присвоить затравку.
методы экземпляра
проверки
в конце
   ложь.
доступ
следующий
   "Lehmer's linear congruential method with modulus m = 2 raisedTo: 16, ? = 27181 odd, and 5 = ? \\ 8, ? = 13849 odd, and c/m approximately 0.21132"
   | врем |
   [
      затравка - 13849 + (27181 * затравка) побитовое и: 8о77777.
      времзатравка / 65536.0.
      врем = 0. ]
         пока истина.
   врем.
собственные
присвоить затравку
   "Чтобы получить псевдо случайное значение затравки, берётся время часов системы. Это значения является большим положительным целым, берутся только 16 младших битов."
   затравкаВремя значение часов в миллисекундах побитовое и: 8о77777.

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

Потоки для наборов без внешних ключей[]

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

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

Допустим создан подкласс Потока который назван Поток связанного списока. Каждый экземпляр хранит ссылку на Связанный список и ссылку на элемент этого набора. Т.к. поддерживается и чтение и запись, то должны быть реализованы сообщения следующий, пом следующим:, в конце и содержимое. (Заметьте что эти четыре сообщения определены в классе Поток как сам ответственность подкласса.) Новый экземпляр Потока связанного списка создаётся при помощи сообщения на: связанный список.

имя класса Поток связанного списка
суперкласс Поток
имена переменных экземпляра набор текущий узел
методы класса
создание экземпляров
на: связанный список
   сам основной новый установить на: связанный список.
методы экземпляра
проверки
в конце
   текущий узел это пусто.
доступ
следующий
   | сохр тек узел |
   сохр тек узелтекущий узел.
   сам в конце ложь: [ текущий узелтекущий узел следующая связь. ].
   сохр тек узел.

пом следующим: связь
   | номер пред связь |
   сам в конце истина: [ набор добавить последним: связь. ].
   номернабор номер для: текущий узел.
   номер = 1
      истина: [ набор добавить первым: связь. ]
      ложь: [ пред связьнабор от: номер - 1. пред связь следующая связь: связь. ].
   связь следующая связь: текущий узел следующая связь.
   текущий узелсвязь следующая связь.
   связь.

собственные
установить на: связанный список
   наборсвязанный список.
   текущий узелсвязанный список первый.

Чтобы продемонстрировать использование этого нового вида Потока создадим Связанный список узлов являющихся экземплярами класса Связь слово; класс Связь слово это подкласс Связи который хранит Цепь или Символ.

имя класса Связь слово
суперкласс Связь
имена переменных экземпляра слово
методы класса
создание экземпляров
для: цепь
   сам новый слово: цепь.
методы экземпляра
доступ
слово
   слово.

слово: цепь
   словоцепь.

сравнение
= связь слово
   слово = связь слово слово.
печать
печатать в: поток
   поток пом следующими все: 'Связь слово для'.
   поток пом следующими все: слово.

Из выше упомянутого видно что экземпляр Связи слова для слова #один создаётся при помощи:

Связь слово для: #один.

Её цепь для печати это:

'Связь слово для один'

Затем можно создать Связанный список из Связей слов и Поток связанного списка которому доступен это Связанный список.

   списокСвязанный список новый.
   список добавить: (Связь слово для: #один).
   список добавить: (Связь слово для: #два).
   список добавить: (Связь слово для: #три).
   список добавить: (Связь слово для: #четыре).
   список добавить: (Связь слово для: #пять).
   доступПоток связанного списка на: список.

Пример последовательности сообщений доступу:

предложение результат
доступ следующий. Связь слово для один
доступ следующий. Связь слово для два
доступ следующий совпадает с: (Связь слово для: #три). истина
доступ пом следующим: (Связь слово для: #вставить). Связь слово для вставить
доступ содержимое. Связанный список (Связь слово для один Связь слово для два Связь слово для три Связь слово для вставить Связь слово для пять)
доступ следующий. Связь слово для пять
доступ в конце. истина

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

Внешние потоки и Поток файла[]

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

Класс Внешний поток это подкласс класса Поток чтения записи. Его предназначение добавить неоднородный протокол доступа. В него входит протокол для позиционирования и для доступа.

Протокол экземпляров Внешнего потока
неоднородный доступ
следующее число: н Возвращает следующие н байтов набора доступного получателю как положительное Малое целое или как Большое положительное целое.
следующим числом: н пом: з Помещает аргумент, з, являющийся положительным Малым целым или Большим положительным целым, в качестве следующих н байтов набора доступного получателю. Если требуется, дополненных нулями.
следующая цепь Возвращает Цепь созданную из следующих элементов набора доступного получателю.
пом следующими цепь: цепь Помещает аргумент, цепь, в набор доступный получателю.
следующее слово Возвращает следующие два байта из набора доступного получателю, представленные как Целое.
пом следующим словом: целое Помещает аргумент, целое, в качестве двух следующих байтов набора доступного получателю.
неоднородное позиционирование
заполнить до: размер пом: знак Пропустить, записывая аргумент, знак, в набор доступный получателю столько раз чтобы указатель ссылка дошёл до границы кратной аргументу размер. Возвращает количество записанных знаков.

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

Advertisement