Н. Вирт - Программирование на языке Модула-2 (1160777), страница 20
Текст из файла (страница 20)
(*В русском переводе книги в приложении 1 приведена таблица перекрестныхссыпок, полученная вручную, поскольку данный вариант программы не воспринимает русскиебуквы. - Прим. перев.*)27. ПОСЛЕДОВАТЕЛЬНЫЙ ВВОД И ВЫВОДПопулярность и удобство языков программирования высокого уровня достигаетсяблагодаря абстрагированию от конкретных свойств ЭВМ, на которых программа выполняется.Однако при этом сохраняются все детали алгоритма, описываемого данной программой. Операцииввода и вывода оказались тем разделом программирования, который дольше всего сопротивлялсявведению абстракций. Это и неудивительно, поскольку ввод и вывод тесно связаны с работойустройств, внешних по отношению к ЭВМ. Структура, Функции и работа этих устройств вбольшой мере зависят от их типа и марки.
Многие языки программирования обычно имеютвстроенные операторы для чтения и записи данных в последовательной Форме без ссылки наконкретные устройства. Такая абстракция имеет много преимуществ, однако всегда существуютприложения, в которых должно быть использовано конкретное свойство какого-либо внешнегоустройства, причем стандартными операторами языка это сделать трудно или вообще невозможно.Кроме того, универсальность обычно ведет к большим накладным расходам, и операции, удобнореализуемые на одних устройствах, могут оказаться неэффективными на других. Следовательно,существует настоятельная потребность сделать свойства отдельных устройств видимыми для техприложений, которые требуют их эффективного использования. Таким образом, упрощение иобобщение за счет опускания деталей вступает в прямое противоречие с требованием доступностидеталей для эффективной реализации.104В Модуле-2 эта глубинная дилемма разрешена (или, точнее, обойдена) за счет того, что вязык вообще не включены операторы ввода и вывода.
Этот крайний подход оказался возможнымблагодаря двум свойствам языка. Во-первых, существует такая конструкция, как модуль,позволяющая строить иерархию (библиотечных) модулей, представляющих различные уровниабстракции. Во-вторых, в языке можно выражать машинно-зависимые операции, такие, каквзаимодействие с внешними устройствами. Эти операции обычно включаются лишь в модулисамого низкого уровня иерархии и, следовательно, их (операции) можно отнести к такназываемым средствами низкого уровня. Программа, не использующая детали работы сконкретными устройствами, импортирует процедуры из стандартных модулей, находящихся навысоких уровнях упомянутой иерархии.
Программа же, требующая высокой эффективности илииспользующая специфические свойства конкретного устройства, может применить либо модулинизкого уровня, так называемые драйверы устройств, либо непосредственно примитивы языка. Впоследнем случае получится немобильная программа, так как будет происходить обращение кособенностям конкретной ЭВМ или операционной системы.В этом контексте невозможно привести примеры операций над внешними устройствами,описываемых на низком уровне иерархии модулей, поскольку существует большое разнообразиетаких устройств.
Мы ограничим дальнейшее изложение, приведя лишь типичную иерархиюмодулей, используемых при выполнении операций стандартного ввода и вывода и описавстандартный модуль InOut, уже встречавшийся в примерах предыдущих разделов. Кроме того,назовем некоторые операции, которые должны быть в любой реализации Модулы.
Однако мы нехотим четко определять ни имена модулей, содержащих эти операции, ни набор остальныхопераций, включаемых в такие модули. Подчеркнем еще раз, что иерархия таких модулей и ихэкспорт не относятся собственно к языку, хотя следует отдавать себе отчет, что без наличия такихмодулей программирование оказалось бы слишком обременительным.В общем случае будем разделять ввод-вывод на видимый и невидимый. Видимый ввод ивывод служат для связи между ЭВМ и пользователем.
В основном его элементы - литеры,имеющие тип CHAR; исключение составляет графический ввод-вывод. Видимые данные - вводчерез клавиатуру, считыватель с перфокарт и т.д.Видимый вывод генерируется дисплеями, печатающими устройствами. Невидимый вводвывод или обмен осуществляется между ЭВМ и так называемыми внешними запоминающимиустройствами, такими, как магнитные диски и ленты. Невидимый ввод может осуществляться сдатчиков (как, например, в лабораториях), а вывод - на устройства.
управляемые ЭВМ, такие, какграфопостроители, заводские сборочные линии, светофоры, сети передачи данных. Данные дляневидимого ввода-вывода могут иметь любой тип, а не только тип CHAR.Подавляющее большинство операций ввода-вывода и видимого, и невидимого можнорассматривать как последовательные. Данные для этих операций имеют тип, который непринадлежит к основным структурным типам Модулы, таким, как массив и запись. Но тем неменее мы дали ему имя. Такой тип называется потоком. Для него характерны следующиеособенности:1.
Все элементы потока имеют один и тот же тип, базовый тип потока. Если этот тип —CHAR, то поток называется текстовым потоком.2. Число элементов потока заранее неизвестно. Следовательно, поток - динамическаяструктура (простейший случай). Число элементов называется длиной потока, и поток с нулевымчислом элементов называется пустым потоком.3. Поток может модифицироваться только добавлением элементов в конец (или удалениемпотока целиком). Добавление элемента называется записью (не путать с типом запись).4. В каждый момент времени виден (доступен) только один элемент, а именно элемент,находящийся в текущей позиции потока. Доступ к этому элементу называется чтениям.
Операциячтения обычно продвигает позицию потока к следующему элементу.1055. Каждый поток имеет режим: поток может либо читаться, либо писаться. Следовательно,каждый поток характеризуется состоянием, содержащим длину, позицию и режим.Заметим, между прочим, что поток в том виде, как описано выше, возможно, наиболееудачный пример абстракции данных. Несомненно, он используется в повседневной практикегораздо шире, чем часто приводимые примеры стеков и очередей. Язык Паскаль включает его внабор своих, основных структурных типов наряду с массивами, записями и множествами.
ВПаскале потоки называются последовательными Файлами, а видимые потоки (с базовым типомCHAR) - текстовыми Файлами.До того как мы начнем рассмотрение иерархии модулей, осуществляющих ввод и вывод,хотелось бы отметить наличие двух различных типов операций ввода-вывода. В особенности этокасается видимого ввода и вывода. С одной стороны, происходит реальная передача данныхмежду ЭВМ и внешними, устройствами.
Сюда же входят такие операции, как запуск и проверкасостояния внешних устройств: клавиатуры, дисплея, печатающего устройства. С другой стороны,необходимы операции преобразования данных из одной формы в другую. Если, например,значение выражения типа CARDINAL нужно выдать на дисплей, то внутреннее представлениенеобходимо преобразовать в литерное в виде последовательности десятичных цифр.
Затем вдисплее происходит перевод литерного представления (обычно содержащего 8 битов на литеру) вматрицу видимых точек или линий. Перевод внутреннего представления числа впоследовательность литер можно считать машинно-независимой операцией, и, следовательно, этоочевидный кандидат на отделение от операций, характерных для устройства. Эта операция можетпроизводиться одной и той же процедурой, независимо от того, будет ли поток запоминаться надиске или высвечиваться на экране дисплея. Такое преобразование называется форматированием.Третий класс функций, которые могут быть легко выделены, относится к устройствам,связанным более чем с одним потоком, в основном это устройства дисковой памяти.
Мыобращаемся в этом случае к операциям выделения дисковой памяти и связывания имен сотдельными потоками или Файлами. Учитывая тот Факт, что потоки (и Файлы) - динамическиеструктуры, приходим к выводу, что операции выделения памяти весьма сложны. Именованиеотдельных Файлов, и в особенности работа с директориями для быстрого поиска отдельногофайла, - вторая задача, требующая тщательно разработанного механизма. И выделение памяти, иработа с директориями - задачи, относящиеся к резидентной части операционной системы.Похоже, что существует столько же способов решения этих задач, сколько и самих операционныхсистем. И ввод-вывод - именно та область, где уровни абстракции операций существенноразличаются в разных операционных системах, чрезвычайно затрудняя выработку обязательныхсоглашений о примитивах работы с Файлами, которые бы были и независимы от операционнойсистемы, и эффективно реализуемы на многих (или хотя бы больше чем на одной) операционныхсистемах.
По этой причине наше решение заключается в том, чтобы предложить иерархиюмодулей, оставив выбор уровня подключения к этой иерархии на усмотрение программиста..Подключение на высоком уровне обеспечивает простоту понятий и мобильность программ(возможно, за счет эффективности); низкий уровень подключения к иерархии открывает передпрограммистом весь диапазон возможностей, предоставляемых операционной системой. Этипреимущества достигаются, однако, за счет меньшей мобильности программ.
В последнем случавпрограммисту настоятельно рекомендуется размещать системно-зависимые операторы вминимально возможном числе мест программы. Вершина нашей иерархии модулей образуетсямодулем InOut. Он содержит два текстовых потока, один из которых - стандартный входнойисточник, а другой -. стандартный выходной поток.
Модуль InOut предлагает следующиевозможности:1. Набор процедур чтения данных из входного потокаФорматированных данных. Это процедурыinпредназначен для вводаRead(ch)ReadString(s)ReadInt(x)106ReadCard(x)Конец потока определяется проверкой экспортируемой переменной Done. Ее значениеравно FALSE, если операция чтения оказалась безуспешной из-за того, что встретился . конецпотока. В этом случае процедура Read(ch) присваивает переменной ch значение 0С.Следовательно, типичная схема программы последовательного ввода имеет видRead(ch);WHILE Done DO обработать(ch); Read(ch);END2. Набор процедур записи в поток out служит для вывода Форматированных данных.
Этоследующие процедуры:Write(ch)WriteStrina(s)WriteLnWriteInt(x,n)WriteCard(x,n)WriteOct(x,n)WriteHex(x,n)3. ПроцедурыOpenInput(s)OpenOutput(s)СloseInputCloseOutputпредназначены для связи файлов со стандартными потоками in и out. Если не вызываласьпрограмма Open Input, то предполагается, что входные данные поступают со стандартноговходного устройства, обычно с клавиатуры оператора.
Вызов OpenInput приводит к запросу именифайла со стандартного входного устройства и привязке потока in к указанному Файлу.Аналогично (до вызова OpenOutput), выходные данные направляются на стандартное выходноеустройство, обычно операторский терминал, а после вызова происходит их привязка к указанномуФайлу.
Вызов процедуры CloseInput (CloseOutput) возвращает ввод (вывод) на стандартноеустройство. Открытые Файлы перед завершением программы должны быть закрыты. В модуле InOutэффективно достигнута независимость от используемой операционной системы посредствомтакой абстракции, как поток, и за счет упрятывания двух стандартных потоков, описания которыхмогут включать характеристики операционной системы. Эти характеристики могут потребоватьсядля достижения эффективности в модулях низкого уровня. Модуль также скрывает такиесистемно-зависимые средства, как способ именования Файлов, операции их открытия и закрытия.Кроме того, одни и те же процедуры Форматирования применяются как при работе с клавиатуройи дисплеем, так и при работе с Файлами. Дальнейшие подробности можно получить изопределения модуля InOut, приведенного в приложении.107Форматный ввод и вывод действительных чиселсопутствующим модулем RealInOut.