Главная
Главная страница, новости
Компьютерные тренажеры
· Обзорная статья
· Конструктор
· Тренажеры (продукты)
· Теплосети
· Как заказать
SCADA Другие проекты
· Сад Слов
· Almik
Публикации
· Основные публикации
· Блог ЖЖ
· Путешествия
· Живопись
. Лирика
Об авторе Rambler's Top100

ЛЕКЦИИ ПО КУРСУ ИНФОРМАТИКИ

Данный курс лекций предназначался для студентов технических специальностей. Хотя за прошедшие годы из-за прогресса вычислительной техники он во многих деталях устарел, но, надеюсь, такие его разделы, как ВВЕДЕНИЕ и ИСПОЛЬЗОВАНИЕ ВЫЧИСЛИТЕЛЬНОЙ ТЕХНИКИ надолго останутся концептуально значимыми.

Ну а раздел ТИПОВЫЕ ЭЛЕМЕНТЫ АЛГОРИТМОВ незаменим на начальном этапе обучения алгоритмизации и программированию. Как показывает мой многолетний опыт преподавания программирования в техническом вузе (от начал информатики до микропроцессорных средств), эти достаточно простые, но важные приемы программирования служат камнем преткновения для очень многих студентов, которым их вовремя не показали, считая тривиальными. Итак, успехов Вам, дорогие читатели!


   УДК 681.3

   Донской А.Н.  Лекции  по   курсу   "Информатика"/   для   студентов
технических специальностей. - Чебоксары, Чуваш. ун-т, 1994,

   Рассмотрены общие   вопросы   информатики;    основные    концепции
программирования    и    методы   повышения   надежности;   прикладное
программирование на  языке  PASCAL;  ряд  простых  и  сложных  приемов
программирования;  структура,  характеристики  и  операционные системы
миниЭВМ и персональных ЭВМ IBM PC.
   Предназначено для  студентов  специальности  21.05 "Электропривод и
автоматизация промышленных установок и технологических комплексов",  а
также других технических специальностей.

                          ЗАДАЧИ КУРСА

   Общий объем лекционного курса - 54 часа.

   Основной задачей  курса  "Информатика"  является   формирование   у
студентов навыков использования вычислительной техники на уровнях:
   - потребления  средств  вычислительной   техники   и   программного
обеспечения;
   - прикладного программирования на языке высокого уровня.
   Данный курс также имеет целью:
   - формирование четкого структурного  и  алгоритмического  мышления,
умения формулировать и решать поставленную задачу;
   - практическое освоение ряда стандартных приемов программирования;
   - знакомство и практическое освоение доступной вычислительной
техники, системного и прикладного программного обеспечения.
   В качестве  базового  языка программирования высокого уровня выбран
PASCAL,  хорошо реализующий концепцию  структурного  программирования.
Приветствуется    также   факультативное   освоение   других   средств
программирования;  по  согласованию  с  преподавателем  для  отдельных
студентов допускается замена базового языка.
   Материал лекций базируется на моем одиннадцатилетнем опыте  профес-
сионального  программирования  на  ЭВМ разных типов и пятилетнем опыте
преподавания данного предмета.  Данный конспект  нельзя  рассматривать
как справочник, например, по языку PASCAL (полную информацию можно по-
лучить на лекции и из других источников); скорее, сделана попытка сис-
темного рассмотрения основных вопросов; также подробно освещены не ле-
жащие на поверхности и отсутствующие  в  популярной  литературе  общие
вопросы и приемы программирования, и информация, полученная на семина-
рах Ассоциации групп  пользователей  Borland.  Статистика  характерных
ошибок и трудностей студентов потребовала  ввести  специальный  раздел
для анализа типовых элементов алгоритмов.

                              СОДЕРЖАНИЕ

1. ВВЕДЕНИЕ  (2 ч)
      Роль информации в жизни человека. Основные качественные
   скачки в информационной истории человечества.  Информатика
   - наука о  получении,  хранении  и  обработке  информации.
   Программирование  ЭВМ  как  раздел  информатики.  Основные
   понятия:   алгоритм,    исполнитель,    программа,    язык
   программирования.

2. ИСПОЛЬЗОВАНИЕ ВЫЧИСЛИТЕЛЬНОЙ ТЕХНИКИ (2 ч)
      Потребление средств    вычислительной     техники     и
   программного   обеспечения;  программирование:  системное,
   художественное,  прикладное,  технологическое. Системное и
   прикладное программное обеспечение.

3. ОРГАНИЗАЦИЯ ЭВМ. ОСНОВНЫЕ ТЕХНИЧЕСКИЕ ХАРАКТЕРИСТИКИ (4 ч)
      Составные части  ЭВМ  и  их  основные   характеристики.
   Характерные  особенности  калькуляторов,  микро-,  мини- и
   больших  ЭВМ.  Персональные  ЭВМ.  Числовые  и  символьные
   данные,  таблицы  кодировки символов.  Организация памяти.
   Основы функционирования ЭВМ.  Машинные команды. Ассемблеры
   и языки высокого уровня.

4. ПРИКЛАДНОЕ ПРОГРАММИРОВАНИЕ (4 ч)
      Этапы решения решения задачи с применением ЭВМ.  Методы
   повышения    надежности   программ.   Основные   концепции
   программирования.

5. ЯЗЫК ПРОГРАММИРОВАНИЯ PASCAL (4 ч)
      Типы данных  в  языке  PASCAL.  Константы и переменные.
   Арифметические типы данных и операции  над  ними.  Правила
   записи арифметических выражений, стандартные функции.

6. ОСНОВНЫЕ АЛГОРИТМИЧЕСКИЕ КОНСТРУКЦИИ  (6 ч)
      Способы описания  алгоритмов:   блок-схема,   словесное
   описание,  программа  на  языке высокого уровня.  Основные
   алгоритмические конструкции:  последовательность действий,
   разветвление,  цикл.  Условный  оператор;  логический  тип
   данных и операции над ними.  Оператор выбора CASE. Циклы с
   постусловием, предусловием и с параметром.

7. УПОРЯДОЧЕННЫЕ ТИПЫ ДАННЫХ - МАССИВЫ  (2 ч)
      Описание и     использование     массивов     различной
   размерности.  Использование символьных массивов для работы
   с текстами.

8. ТИПОВЫЕ ЭЛЕМЕНТЫ АЛГОРИТМОВ (2 ч)
      Выделение разрядов    числа.    Определение   четности.
   Начальные значения. Вычисление степени (-1). Периодическое
   изменение  знака (триггер).  Вещественные циклы с заданным
   количеством повторений.  Вычисление периодических  функций
   (приведение аргумента в заданный диапазон).  Использование
   промежуточных  (буферных)  переменных:   обмен   значений,
   вычисления  по  рекуррентным формулам.  Вставка и удаление
   элемента в массив, или техника указателей.

9. ПОДПРОГРАММЫ  (4 ч)
      Выгода от   использования   подпрограмм.  Классификация
   подпрограмм:  по  расположению  и  способу  описания,   по
   способу  использования (процедуры и функции).  Вложенность
   подпрограмм,  области действия переменных  и  подпрограмм.
   Способы   передачи  параметров  в  подпрограммы.  Передача
   массивов в подпрограммы в качестве параметров.

10. ВВОД И ВЫВОД ДАННЫХ (2 ч)
      Физический и  логический  аспект ввода и вывода.  Файл,
   начальные понятия о файловой системе.  Способы организации
   файлов. Порядок работы с файлами. Стандартный ввод/вывод в
   языке PASCAL.

11. СОСТАВНЫЕ СТРУКТУРЫ ДАННЫХ (ЗАПИСИ) В ЯЗЫКЕ PASCAL (1 ч)

12. ДИНАМИЧЕСКИЕ СТРУКТУРЫ ДАННЫХ (3 ч)
      Динамические переменные,    указатели.    Создание    и
   освобождение  динамических  переменных.  Связные   списки,
   деревья.

13. РЕКУРСИЯ  (2 ч)
      Понятие рекурсии.   Характерные   области    применения
   рекурсивных  алгоритмов:  обработка изображений,  работа с
   деревьями, интерпретация текстов программ.

14. ОПЕРАЦИОННЫЕ СИСТЕМЫ  (2 ч)
      Назначение, функции  и  структура  операционных систем.
   Основные компоненты операционных систем: файловая система,
   драйверы   внешних   устройств,   монитор,   интерфейс   с
   оператором, системные программы общего назначения.

15. ТРАНСЛЯТОРЫ ЯЗЫКОВ ВЫСОКОГО УРОВНЯ (2 ч)
      Трансляторы: интерпретаторы    и    компиляторы.   Виды
   генерируемого   компиляторами   кода.   Этапы   подготовки
   компилируемых программ к выполнению на ЭВМ.

16. ОПЕРАЦИОННЫЕ СИСТЕМЫ ТИПА RT-11 ДЛЯ МИНИ- И МИКРОЭВМ (2 ч)
      Классификация, возможности;      файловая      система;
   устройства и драйверы;  мониторы; командный язык. Основные
   команды.  Редакторы   текстов.   Управление   дисплеем   и
   клавиатурой.

17. ФОРМЫ ОРГАНИЗАЦИИ ДИАЛОГА (2 ч)
      Понятие дружественного    интерфейса    программ     на
   персональных ЭВМ. Формы организации диалога: вопрос-ответ,
   команды оператора, меню, экранные формы.

18. КРАТКОЕ ЗНАКОМСТВО С ПЕРСОНАЛЬНЫМИ ЭВМ ТИПА IBM PC (4 ч)
      Операционные системы, системы управления базами данных,
   электронные таблицы, издательские системы и т.д.

19. TURBO PASCAL ДЛЯ IBM PC  (4 ч)
      Системы программирования  и  фирмы.  Особенности  языка
   Turbo Pascal. Работа в интегрированной среде Turbo Pascal.
   Использование справочной системы. Библиотеки Turbo Pascal.
   Пример интерактивной программы.

ЛИТЕРАТУРА


                          1. ВВЕДЕНИЕ  (2 ч)

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

-----------------------------------------
  О сложности философского осмысления понятия информации и вытекающих
последствиях автор неоднократно выступал в эхоконференциях FIDO.
См., например:


А.Донской - С.Рычик об информации и не только
в FIDO7.SU.PHILOSOPHY


А.Донской - В.Горбачев об информации
в FIDO7.SU.PHILOSOPHY -
скачать зазипованный текст (выборочный)
-----------------------------------------

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

      ┌───────────────────────────────────────────────────────╖
      │        Первоосновой всей живой природы является       ║
      │      активное использование   И Н Ф О Р М А Ц И И     ║
      ╘═══════════════════════════════════════════════════════╝
   Самый примитивный из возможных организмов -  это  вирус,  состоящий
практически только из одной ДНК. Следовательно, он имеет только инфор-
мацию о самом себе и только один механизм ее обработки -  многократное
копирование и воспроизведение самого себя. Примечательно, что для это-
го он использует окружающие его живые организмы, которые содержат дру-
гие необходимые для воспроизведения информации инструменты.
   Если мы  рассмотрим  такие  сложные формы жизни,  как животные,  мы
увидим:
   - у  них  есть  рецепторы и эффекторы - глаза,  лапы и прочее,  что
позволяет им принимать и отдавать информацию в окружающую среду;
   - у   них   есть  специальный  орган  для  хранения  и  переработки
информации - головной мозг;
   - благодаря  мозгу  у  них  есть  способ  получения  и  переработки
информации, заключающийся в наблюдении окружающей среды, запоминания и
применения на практике информации о ней.
   Чем человек отличается от животного?  У него есть (то ли благо,  то
ли ненужное бремя?) сознание,  как  управляемый  механизм  переработки
информации  -  абстрактно-логическое  мышление.  Благодаря ему человек
гораздо интенсивнее перерабатывает информацию и,  разумеется,  гораздо
сильнее  изменяет окружающую среду.  Если вирус способен воспроизвести
только сам себя,  то животное  воспроизводит  часть  окружающей  среды
(жилище),   а   человек  умудрился  воспроизвести  собственный  способ
логического мышления, создав вычислительные машины.
   Нам пока  достоверно  неизвестны  более  высокие  формы организации
живой материи, как и более действенные способы переработки информации,
но практически все  древние  религии  и  восточные  системы  философии
рассматривают в качестве окружающей среды всю Вселенную,  а в качестве
лучшей альтернативы логическому мышлению - интуицию (или  божественное
откровение -  термин  зависит  лишь  от  компетенции  многих поколений
пересказчиков, а суть остается неизменной). Утверждается, в частности,
что умение  отключать  мешающее  сознание  выводит  человека  на новый
уровень информационного бытия;  в качестве же инструмента, помогающего
отключить  сознание,  может  выступать  гипноз, медитация, религиозный
экстаз  и  т.п.    Если   логическое   мышление   оказалось   способно
воспроизвести само  себя,  то интуиция,  вероятно,  позволяет человеку
слиться в единое целое со всем  окружающим  миром.  И  таких  ступеней
вверх может быть бесконечное множество.

   Может быть,  Вы  предложите  теперь  перейти от философии поближе к
предмету, но  избавиться  от нее все равно не удастся.  Ибо вся,  даже
повседневная жизнь человека - это информация,  информация, информация.
И в древности за информацию платили кровью,  а теперь на такой вершине
современного бизнеса,  как банки и биржи,  информация приобрела вполне
осязаемые свойства товара.
   Итак, наконец, дадим определение нашему предмету:
   ИНФОРМАТИКА - это наука о способах получения, хранения, переработки
и передачи информации.  Вместе с философией, задающей во всех областях
науки стратегию  научного  поиска,  и  математикой,  дающей  тактику и
инструмент для   этого   поиска,   информатика   позволяет    грамотно
организовывать информацию   во   всех  сферах  деятельности  -  связь,
экономика, технический эксперимент и пр.
   Проследим же  историю  развития   человечества   с   точки   зрения
информатики:
───────────────┬──────────────────────────────────────────────────────
этапы и их     │       новые способы и инструменты
основные       ├─────────────────────────────┬────────────────────────
особенности    │ получения и переработки     │ хранения
               │ информации                  │ информации
───────────────┼─────────────────────────────┼────────────────────────
животный       │ наблюдение, опыт            │ мозг
               │                             │
речь           │ речь, абстрактное мышление  │
               │                             │
письменность   │ письменность, логическое    │ материальные
               │ мышление, зачатки наук,     │ носители
               │ научные приборы             │ письменности
               │                             │
математизация  │ математика как универсальный│ массово тиражируемые
всех наук      │ язык науки, вычислительные  │ книги
               │ инструменты                 │
               │                             │
информатизация │ ЭВМ, различные средства     │ ЭВМ
всей жизни     │ коммуникации                │
───────────────┴─────────────────────────────┴────────────────────────
   Если математика стала  универсальным  языком  и  инструментом  всех
наук, то   возникшие   почти   на   наших   глазах  ЭВМ  первоначально
считались универсальным   инструментом    для    математики.    Однако
действительность оказалась гораздо интереснее. ЭВМ стали универсальным
инструментом для  передачи,   хранения   и   переработки   информации.
Например, данный  конспект  лекций  набран  на  ЭВМ,  которая в данном
случае выступает как средство хранения информации.  Далее текст пойдет
в типографию (передача и воспроизведение информации).  Другой пример -
безналичный расчет с помощью кредитных карточек.  Продажа  билетов  на
самолет. Музыкальный синтезатор. Компьютерный мультфильм.
   Интересный парадокс  связан  с внедрением в развитых странах безбу-
мажных технологий - количество потребляемой бумаги резко возросло. Это
происходит  из-за  того,  что  большинству  людей приятнее и привычнее
иметь дело с бумагой, несмотря на стоящий на столе персональный компь-
ютер. А доступность этой оргтехники приводит к тому, что любую бумажку
проще послать по факсу, чем тащить на соседний этаж своего учреждения.
   Первые вычислительные машины были сделаны  на  электронных  лампах,
отсюда и   устоявшееся  название  -  электронно-вычислительная  машина
(ЭВМ). На западе более употребителен термин "компьютер"  (буквально  -
"вычислитель"), что  также  не  вполне отражает сущность предмета.  Мы
будем пользоваться термином ЭВМ (для экономии места).
   ПРОГРАММИРОВАНИЕ -    это    раздел    информатики,     посвященный
специфическим  вопросам  обработки  информации  на  ЭВМ.  К сожалению,
хорошая в сущности  идея  -  ввести  предмет  "информатика"  в  школах
обернулась  в  большинстве  случаев профанацией предмета и примитивным
сведением его к программированию на языке BASIC (или еще хуже, FOCAL),
что 80%  людей никогда в будущем не понадобится. Наш курс, напротив, в
большей степени  посвящен  именно  программированию,   поскольку   оно
сегодня занимает весьма важное место в инженерной практике.
   Напомним основные понятия программирования:
   АЛГОРИТМ -  определенная  последовательность  действий,  выполнение
которой приводит  к  нужному  изменению  (или сохранению) информации в
некоторой системе.
   ИСПОЛНИТЕЛЬ -  некоторая  система  (живая  или   техническая)   для
исполнения алгоритмов.   Исполнитель   понимает  какой-либо  язык,  на
котором можно формулировать предлагаемый ему алгоритм.
   ПРОГРАММА -  алгоритм,  выраженный  на языке,  понятном конкретному
исполнителю.
   Пример простейшего исполнителя - лампочка.  Она понимает только две
команды, подаваемые посредством выключателя -  включить  и  выключить.
Человек может  в  зависимости от ситуации понимать команды на огромном
количестве языков и является поэтому универсальным  исполнителем.  ЭВМ
также является  универсальным  исполнителем  в  том смысле,  что может
исполнять огромное количество разнообразных алгоритмов;  но ЭВМ  имеет
только один понятный ей язык - систему команд процессора.
   ЯЗЫК ПРОГРАММИРОВАНИЯ  -  это  и  строго  формализованный  язык для
записи алгоритмов,  и специальная  программа  трансляции  алгоритма  с
данного языка в систему команд ЭВМ.  Для удобства работы программистов
создано большое количество различных языков  программирования,  каждый
из которых удобен для специалиста какой-либо отдельной области науки и
техники и имеет массу недостатков при  применении  в  другой  области.
Делались   попытки   создания  универсальных  языков  программирования
(ALGOL, PASCAL),  удобных  для  символической  записи  алгоритмов,  но
конкретные трансляторы все равно в той или иной степени привязывают их
к какой-либо  области  науки  и  к  особенностям  конкретных  ЭВМ  для
повышения эффективности программ.


            2. ИСПОЛЬЗОВАНИЕ ВЫЧИСЛИТЕЛЬНОЙ ТЕХНИКИ (2 ч)

   Термины и определения:
   ПОЛЬЗОВАТЕЛЬ -   человек,  использующий  в  своей  работе  ЭВМ  как
инструмент для решения различных задач.
   ОПЕРАТОР - человек,  сидящий в данный момент за терминалом ЭВМ. Это
может быть  работник  на АРМ,  или пользователь,  решающий на ЭВМ свою
прикладную задачу - мы будем называть их  операторами,  когда  захотим
подчеркнуть, что речь идет о диалоговом взаимодействии человека с ЭВМ.
   ПРИКЛАДНОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ (ППО) - программы, предназначен-
ные для решения конкретных  (прикладных)  задач,  превращающие  ЭВМ  в
удобный  инструмент  для пользователя.  Пользователь ППО не занимается
программированием и не всегда даже знает о том, что использует ЭВМ.
   ПРОБЛЕМНО-ОРИЕНТИРОВАННОЕ  ПРОГРАММНОЕ  ОБЕСПЕЧЕНИЕ     (ПОПО)    -
программы,     помогающее     пользователю     решать    нестандартные
(исследовательские) задачи.  Обычно при этом  пользователь  занимается
программированием в   терминах   и   понятиях,  приближенных  к  своей
предметной области (специальности).
   АВТОМАТИЗИРОВАННОЕ РАБОЧЕЕ МЕСТО  (АРМ) - рабочее место какого-либо
работника, основную  часть  которого  составляет   универсальная   или
специализированная  ЭВМ.  Под АРМ также понимают комплексы ППО и ПОПО,
позволяющие   пользователям   выполнять   пректно-конструкторские    и
исследовательские  работы  на  универсальных ЭВМ.  АРМ характеризуются
интенсивной диалоговой работой пользователя с программой.
   СИСТЕМНОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ  -   это   операционная   система
(комплекс программ,  обеспечивающих функционирование ППО на конкретной
ЭВМ, управляющих ресурсами ЭВМ и обеспечивающих диалог с  оператором).
К системному    программному    обеспечению   иногда   относят   также
вспомогательные программы вроде текстовых  редакторов,  и  трансляторы
языков программирования,   обеспечивающих   пользователю   возможность
разработки нового ППО.
   МИКРОПРОЦЕССОРЫ -   специализированные   или   универсальные   ЭВМ,
выполненные в виде интегральных микросхем, миниатюрные размеры которых
позволяют встраивать их в различные технические изделия.
   ЯЗЫКИ  НИЗКОГО  УРОВНЯ    (АССЕМБЛЕРЫ)    -    предназначены    для
программирования ЭВМ  (микропроцессоров)  в их системе команд.  При их
использовании программист  формулирует  алгоритм  на  уровне  машинных
команд,  учитывает особенности конкретного процессора,  в той или иной
степени заботится о распределении памяти,  использовании  регистров  и
т.п. Языки низкого уровня обычно используются в следующих случаях:
   - при  создании  базового  программного  обеспечения  (операционной
системы) для нового разрабатываемого процессора ЭВМ;
   - при   программировании    специализированных    микропроцессоров,
устройств ЧПУ  с  целью  минимизации  размеров  управляющих программ и
повышения их эффективности;
   - для повышения эффективности ППО универсальных ЭВМ.
   УНИВЕРСАЛЬНЫЕ ЯЗЫКИ ВЫСОКОГО УРОВНЯ - языки,  при работе на которых
нет необходимости  учитывать  особенности  конкретной ЭВМ и ее систему
команд. Программист  формулирует   алгоритм   с   помощью   достаточно
обобщенных понятий,  таких,  как  "арифметическое  варажение",  "вывод
результата" и  т.п.,  что  позволяет   легко   программировать   самые
различные алгоритмы   (универсальность),   не   вдаваясь   в  излишнюю
детализацию.

   Обобщенную схему   использования   ЭВМ   в   жизни  человека  можно
представить таким образом:

        ┌────────────────╖    ┌──────────────────────────────────────╖
уровни: │  потребление   ║    │   п р о г р а м м и р о в а н и е    ║
        ╘══════╤═══════╤═╝    ├────────┬──────────┬─────────┬────────╢
               │       │      │техноло-│прикладное│системное│художе- ║
               │       │      │гическое│          │         │ственное║
               │       │      ╘═╤══════╧══════╤═══╧═══╤═════╧══╤═════╝
               │       │        ├─────┬───────│───────│────────│─┐
               │       └────┐   │     │       │       ├──────┐ │ │
               │            │   │     │  ┌────┤       │  ┌───│─┤ │
          ┌────┴──────────┐┌┴───┴───┐ │  │ ┌──┴───────┴──┴┐┌─┴─┴─┴─┐
средства: │микропроцессоры││АРМ на  │┌┴──┴┐│универсальные ││языки  │
          │в быту и       ││базе ЭВМ││ПОПО││языки высокого││низкого│
          │на производстве││(ППО)   │└┬──┬┘│го уровня     ││уровня │
          └────┬──────────┘└┬───┬───┘ │  │ └──┬───────┬──┬┘└─┬─┬─┬─┘
               │  ┌─────────┘   ├─────┘  ├────┘       ├──────┘ │ │
               │  │  ┌───────────────────┤    ┌───────│──┴─────┘ │
               │  │  │          ├────────│────│───────│──────────┘
             ┌─┴──┴──┴──╖┌──────┴────╖┌──┴────┴╖┌─────┴────────────╖
цели и       │решение   ║│создание   ║│создание║│создание средств  ║
результаты:  │конкретной║│управляющих║│ППО и   ║│программирования  ║
             │задачи    ║│программ   ║│ПОПО    ║│(ОС, языков, ПОПО)║
             ╘══════════╝╘═══════════╝╘════════╝╘══════════════════╝

   Рассмотрим наиболее   характерные  цепочки  на  приведенной  схеме.
Первая такая цепочка: потребление-микропроцессоры-конкретная задача.
   С движением     научно-технического     прогресса     все     более
распространяется   микропроцессорная   техника.    Она    используется
практически   в  любом  современном  изделии  -  автомобиль,  аудио  и
видеоаппаратура, станок с числовым  программным  управлением  (ЧПУ)  и
даже бытовые   кухонные   приборы.  Характерны  следующие  особенности
микропроцессорной техники:
   - при   ее   использовании   многократно   улучшаются  технические,
экологические, экономические и эргономические характеристики изделий;
   - появляются качественно новые возможности давно известных изделий;
   - подавляющее  большинство  пользователей  этой  техники  даже   не
подозревает о ее существовании.
   Наша специальность на сегодняшний  день  в  основном  и  занимается
применением микропроцессорной  техники в технологическом оборудовании.
Основные задачи, которые при этом решаются - это управление различными
видами электрического  привода  (от  двигателя  кухонного  миксера  до
до мощного насоса  системы  водоснабжения,  от  привода  телескопа  до
приводов промышленных роботов, конвейеров и т.д.).

   Вторая цепочка: потребление-ППО-конкретная задача.
   Эта цепочка также распространена  очень  широко  и  характеризуется
явным использованием   ЭВМ  в  качестве  автоматизированного  рабочего
места (АРМ). Например:
   - кассир, продающий билеты на поезд или самолет;
   - служащий банка, бухгалтер предприятия и т.п.;
   - композитор,  работающий  на современном синтезаторе,  управляемом
ЭВМ;
   - инженер,  проектирующий с помощью АРМ что угодно - от самолета до
разводки печатных плат электронной техники;
   - студент,  рассчитывающий  курсовой  проект  с  помощью карманного
калькулятора или специальной программы на большой ЭВМ.
   Два последних примера вполне характерны и для нашей специальности.

   Следующая цепочка   представляет   одну  из  основных  задач  нашей
специальности: технологическое программирование.
   Технологическое программирование  имеет  целью создания управляющих
программ для    различного     технологического     оборудования     и
микропроцессорной техники. Вот различные его варианты:
   - создание программы для станка с ЧПУ для обработки на этом  станке
конкретного изделия (решается в основном на АРМ);
   - создание   программы   для   микропроцессоров,   встраиваемых   и
управляющих каким-либо  изделием.  Эта задача может решаться на низком
уровне (путем  непосредственного  программирования  на  языке  низкого
уровня -  ассемблере,  либо  на  более  высоком  уровне  -  с  помощью
специализированных   средств,   позволяющих   автоматизировать    этот
трудоемкий    процесс    -   проблемно-ориентированного   программного
обеспечения или ПОПО).

   Теперь рассмотрим  ситуацию:  пусть   у   какого-либо   специалиста
(например, инженера, научного работника или студента) возникла задача,
требующая каких-либо расчетов (прикладная  задача).  Специалист  может
пойти различными путями:
   - если  для  решения  аналогичных  задач  есть   АРМ   или   просто
специальная программа  на  ЭВМ  (прикладное  программное обеспечение -
ППО), то он пойдет по пути потребление-ППО-конкретная задача;
   - если  нужного  ППО нет,  ему придется заняться программированием.
При этом он может использовать  универсальные  языки  высокого  уровня
(BASIC, PASCAL  и  т.п.),  либо  проблемно-ориентированное программное
обеспечение (ПОПО),  которое позволяет ему программировать свою задачу
в терминах своей специальности;
   - наконец,  результатом программирования может быть как однократное
решение задачи  с  помощью написанной программы,  так и создание ППО и
даже ПОПО,  которое облегчит ему и его  коллегам  решение  аналогичных
задач в   будущем.   Как  правило,  это  наиболее  полезный  результат
деятельности специалиста. Для его достижения иногда придется расширять
рамки исходной   задачи,  предусматривая  достаточную  универсальность
программы, но   в   дальнейшем   такой   перерасход   усилий    обычно
оправдывается.

   Уровень системного   программирования   требует   специальных    (и
глубоких!)  знаний  аппаратной  части  ЭВМ  и  принципов построения ее
базового программного обеспечения.  Как ясно из самого названия, такие
программисты занимаются  написанием  (и эксплуатацией - что на больших
ЭВМ зачастую  не  менее   сложно)   операционных   систем.   В   среде
профессиональных программистов  "системщики" пользуются особым (вполне
заслуженным) уважением.
   Характерно, что   в  сегодняшних  условиях  существования  науки  и
техники технологическое программирование также приближается по  уровню
к системному.  В  нашей стране,  в силу ее технологической отсталости,
этой тенденции способствует отсутствие  АРМ,  помогающих  разработчику
решать задачи технологического программирования.

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


    3. ОРГАНИЗАЦИЯ ЭВМ. ОСНОВНЫЕ ТЕХНИЧЕСКИЕ ХАРАКТЕРИСТИКИ (4 ч)

   Настало время  поближе  познакомиться  с универсальным инструментом
для обработки информации - ЭВМ. ЭВМ имеет следующие составные части:

       Центральный Процессор, ЦП (Central Processing Unit, CPU)

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

                           Память (memory)

   Предназначена для хранения информации:
   а) внутренняя  память  -  предназначена  для  хранения  программ  и
данных,  с которыми ЭВМ работает в данный момент времени.  У некоторых
ЭВМ  (например,  калькуляторов)  существует  две  разных  памяти - для
программы и для данных.  Большинство же современных ЭВМ используют для
этого одну и ту же память.  Кроме упрощения работы с памятью, это дает
возможность программе изменять саму себя,  что во многих случаях очень
удобно  и  даже  необходимо.  Есть и вредные стороны такой организации
памяти - она позволяет писать вирусы,  изменяющие сами себя  и  другие
программы (если ЭВМ от этого не защищена).
   Внутренняя память бывает двух видов:
   - Оперативное  Запоминающее Устройство,  ОЗУ (Random Access Memory,
RAM) является  "рабочим  полем"  для  процессора  и   хранит   текущую
программу и ее данные.  В ОЗУ можно записывать и считывать информацию.
При выключении компьютера содержимое ОЗУ стирается;
   - Постоянное Запоминающее Устройство,  ПЗУ (Read Only Memory,  ROM)
проедназначено для  хранения базовых компонентов операционной системы,
реализующих простейшие операции ввода-вывода,  управления  дисплеем  и
другими устройствами.  Программа,  записанная  в  ПЗУ,  при выключении
питания не стирается (в отличие от содержимого ОЗУ).  Но в ПЗУ  нельзя
записать  информацию,  ее  можно  оттуда только прочитать.  На бытовых
компьютерах в качестве операционной системы выступает "зашитый" в  ПЗУ
интерпретатор  таких языков,  как BASIC,  FOCAL и т.п.  Содержимое ПЗУ
сама ЭВМ изменить не может,  так что ПЗУ можно  отнести  к  аппаратной
части   ЭВМ.   Основные  характеристики  внутренней  памяти:  объем  и
организация адресного пространства; о них поговорим позже;
   б) внешняя   память   предназначена  для  долговременного  хранения
информации. К ней относятся:
   - жесткие диски (Hard Disk,  HD) - основное запоминающее устройство
более или менее серьезных ЭВМ.  Жесткие диски типа  "винчестер"  имеют
достаточно большую емкость (20-1000М).  Для сравнения приведем размеры
программ для IBM PC: красивые современные игровые программы (0.5-35М),
система    программирования    Turbo    Pascal   6.0   (2М),   система
программирования Borland Pascal 7.0 (порядка 30М);
   - гибкие  диски,  дискеты  (Floppy  Disk,  FD)  - предназначены для
архивного хранения информации и для переноса ее с одной ЭВМ на другую.
Стандартные для  IBM  PC дискеты - диаметром 3"5 (1.4М) и 5"25 (1.2М).
Не все дискеты имеют такие характеристики - их емкость зависит от типа
и качества   магнитного  слоя  на  поверхности  диска.  Маркировка  на
дискетах обозначает: 1D или SD - одинарная плотность (360К), 2D или DD
- двойная  плотность  (720К),  4D или HD - высокая плотность (1.2М или
1.4М);
   - магнитофоны (единственное запоминающее устройство для  простейших
бытовых компьютеров); а также специальные устройства - стриммеры - для
хранения очень больших объемов информации;
   - поключаемые   к  ЭВМ  картриджи,  содержащие  ПЗУ  с  какими-либо
программами. Широко используются как в игровых компьютерах,  так  и  в
промышленном технологическом    оборудовании.    Отличаются    высокой
надежностью;
   - ныне устаревшие носители информации вроде перфокарт и перфолент;
   - современные   носители   информации   вроде   лазерных    дисков,
отличающихся огромной емкостью и высокой надежностью (CD-ROM - Compact
Disk Read Only Memory).

          Устройства, обеспечивающие интерфейс с оператором

   Понятие "интерфейс"   (interface)   означает  устройство  и  способ
взаимодействия какой-либо технической системы с человеком  или  другой
системы. Интерфейс ЭВМ с оператором обеспечивают следующие устройства:
   - клавиатура (keyboard) - устройство ввода символьной информации  и
ряда управляющих команд;
   - дисплей (display) - телевизионный монитор как  устройство  вывода
символьной  и   графической    информации.    Распространены   дисплеи
алфавитно-цифровые (символьные),  графические и  комбинированные.  Для
графических основными     характеристиками     являются    разрешающая
способность (количество точек по вертикали и горизонтали) и количество
цветов. Например,  на IBM  PC  используются  адаптеры  мониторов:  CGA
(320x200, 4 цвета);  EGA (640x350, 16 цветов); VGA (640x480, 16 цветов
или 320x200, 256 цветов), SVGA (800х600, 1024x720 и т.д.). В некоторых
АРМ используются также дисплеи векторного типа, рисующие не точками, а
отрезками линий;
   - принтер  (printer)  - печатающее устройство,  предназначенное для
вывода символьной и графической информации на бумагу.  На больших  ЭВМ
применяются  высокоскоростные  (десятки и более строк в секунду) алфа-
витно-цифровые печатающие устройства, АЦПУ. На ПЭВМ чаще всего исполь-
зуют дешевые игольчатые печатающие устройства, позволяющие печатать не
быстрее, чем строку в секунду, но зато как текст, так и графику. Доро-
гие лазерные принтеры печатают быстро (до нескольких листов в секунду)
и с качеством выше типографского.  Наконец,  цветные лазерные принтеры
позволяют  даже  печатать  фальшивые  деньги.  Основные характеристики
принтеров: возможность печати графики, система команд и скорость печа-
ти.  Наиболее  распространенные  матричные (игольчатые) принтеры имеют
Epson-совместимую систему команд, позволяющую специальными управляющи-
ми кодами включать разные шрифты,  графику, подчеркивание и пр. Лазер-
ные принтеры поддерживают  специальные  языки  программирования  (типа
PostScript), позволяющие описывать сложные графические рисунки на лис-
те бумаги и различные шрифты типографского качества;
   - манипулятор типа "мышь" (mouse) очень  прост  в  использовании  и
удобен при работе с графическими диалоговыми программами;
   - манипулятор типа "джойстик"  (joystick),  похожий  на  самолетную
ручку управления, используется в игровых программах;
   - графопостроители и плоттеры, рисующие на бумаге линиями;
   - ряд   устройств   для   генерации   звука   -    от    простейшей
пьезоэлектрической пищалки  до  специальных  звуковых  плат типа Sound
Blaster и MIDI-интерфейса для синтезаторов.

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

                          Прочие устройства

   Чрезвычайно разнообразны  устройства,  которые  можно  подключить к
ЭВМ. Например,  ЭВМ  может  управлять  технологическим  оборудованием,
научными и   медицинскими   приборами,   обрабатывать  и  генерировать
видеоизображение (20%   телевизионной   рекламы   представляют   собой
компьютерные мультфильмы). Отдельно следует упомянуть устройства связи
с другими ЭВМ, например, модемы (МОдуляторы-ДЕМодуляторы) для передачи
информации по телефонной сети,  благодаря которым можно,  к примеру, с
помощью домашней ЭВМ обменяться  сообщениями  с  коллегами  на  другом
конце земного шара или получить свежую биржевую сводку по коммерческой
компьютерной сети.

              Классы ЭВМ и их технические характеристики

   До недавнего времени было принято рассматривать поколения ЭВМ по их
элементной базе:
   1) на электронных лампах;
   2) на отдельных транзисторах;
   3) на микросхемах, содержащих десятки и сотни транзисторов;
   4) на микропроцессорах,  где  почти  вся  ЭВМ  умещается  на  одном
кремниевом кристалле;
   5) о предстоящем создании  ЭВМ  5  поколения  несколько  лет  назад
заявляли японские   корпорации   (рассматривая,   однако,  в  качестве
классообразующей характеристики  интеллектуальный  интерфейс   ЭВМ   с
оператором). Отдельные элементы таких ЭВМ уже функционируют, например:
pen-computers - карманные блокноты с процессором Intel-486,  4-16М ОЗУ
и  жестким  диском,  распознающие рукописный текст;  или автомобильные
компьютеры, распознающие слова своего хозяина.
   Мне довелось  работать  на советской ЭВМ второго поколения "Мир-2",
которая вычислила мне число "пи" с точностью 150 десятичных цифр за 20
минут;  которая  могла аналитически вычислять неопределенные интегралы
простых функций,  но не могла нарисовать  на  дисплее  лабиринт  10x10
(подробных технических  характеристик  я тогда не знал).  Мне довелось
вырезать  бритвой  дырки  на  старых  перфолентах  и  перфокартах  ЭВМ
третьего  поколения  ЕС-1020  (так  как  перфокарты  были  в  жестоком
дефиците). На ЭВМ четвертого поколения я в данный  момент  работаю;  а
весной  1993  года  я  присутствовал  на  презентации  pen-computer'а.
Правда,  на настойчивые просьбы зала продемонстрировать  распознавание
рукописного текста докладчики никак не отреагировали.
   Итак, коротко опишем особенности  тех  ЭВМ,  на  которых  мы  будем
работать.
   МИКРОКАЛЬКУЛЯТОР -   простейшая   карманная   ЭВМ   для  выполнения
арифметических и некоторых инженерных расчетов. Как и любая ЭВМ, имеет
процессор, ОЗУ   в   виде  одного  или  нескольких  регистров  памяти,
клавиатуру и дисплей. О быстродействии говорить имеет смысл только для
программируемых калькуляторов;  разумеется, оно не выдерживает никакой
критики. Также  как  и  возможный  объем  программы.  Тем  не   менее,
калькуляторы вполне целесообразно применять как при совершении покупок
в магазине, так и для простейших расчетов - ибо калькулятор всегда под
рукой, а  персональный компьютер на сегодня - предмет непозволительной
роскоши.
   МАЛЫЕ ЭВМ  типа  СМ-4,  СМ-1420 являются аналогами PDP-11 фирмы DEC
(Digital Equipment  Corporation).  Сама  ЭВМ  третьего поколения,  она
размещается в небольшой комнате в нескольких небольших шкафах.  В мире
эти  ЭВМ  применяются  в  промышленности  и  в  управлении,  а также в
образовании.  Характерной их чертой является  возможность  подключения
нескольких  терминалов и наличие соответствующих многопользовательских
операционных систем (RT-11,  RSX-11,  Unix).  Наша основная  машина  -
СМ-1420 - имеет около 15 терминалов,  2М ОЗУ и 6 жестких дисков по 2М,
плюс 1 дисковод на 29М.  Если учесть,  что  операционная  система  NTS
(аналог  TS-монитора  RT-11) дает каждому пользователю максимум по 56К
памяти,  то можно сказать,  что ОЗУ  у  нашей  ЭВМ  в  избытке,  а  ее
быстродействие ограничивается    лишь    быстродействием    процессора
(800тыс.операций в секунду) и каналов связи с терминалами. Работать на
ней  одновременно  с  10  другими пользователями - удовольствие не для
слабонервных.
   БОЛЬШИЕ ЭВМ тип ЕС-1022, ЕС-1036 также третьего поколения, но имеют
не полупроводниковую,  а ферритовую память, размещающуюся в нескольких
больших шкафах в большой комнате (более 50 кв.м).  ЭВМ  ЕС-1022  имеет
512К ОЗУ и около 6 терминалов. На сегодняшний день устарела морально и
физически. ЕС-1036 функционирует,  но надежность ее  настолько  низка,
что в течение часа она может неоднократно зависать.
   БЫТОВЫЕ ЭВМ  -  предмет  моего  вожделения  в  студенческие  годы и
совершенно заурядная вещь сегодня.  Сначала ЭВМ типа "Радио-86-РК"  на
микропроцессоре К-580       собирали      некоторые      радиолюбители
(преимущественно с оборонных заводов - где еще можно  было  приобрести
комплектующие). Затем   наша   электронная   промышленность  выпустила
БК-0010 (за которым я ездил в Москву  и  стал,  вероятно,  первым  его
владельцем в   г.Чебоксары)   [8].  Далее  лавина  нарастала  -  рынок
заполнился бытовыми ЭВМ типа  "Микроша",  "Криста";  в  это  же  время
несколько    человек    (в    частности,   выпускник   нашей   кафедры
электропривода)  одновременно  организовали  производство   "Дельты-С"
(ZX-Spectrum,  процессор  Z-80).  Пути товара неисповедимы - вот уже в
Омске функционирует клуб пользователей "Дельты-С".
   Бытовые ЭВМ  не  дотягивают  до  персональных главным образом из-за
отсутствия не  только  винчестера,  но  иногда  и  простого  дисковода
(основным запоминающим устройством является бытовой магнитофон).  Зато
главным их достоинством  является  графический  дисплей  и  дешевизна.
Поэтому  "Дельта-С" - это фактически игровой автомат с большим набором
фирменных игрушек.  На БК-0010  фирменных  игрушек  нет,  но  достойны
глубочайшего  уважения  программисты,  создавшие  для  его  16К памяти
тысячи достаточно сложных игрушек.  В последнее время появились жалкие
подобия IBM  PC  -  "Поиск"  и пр.  Никому не советую их приобретать -
меньше нервов  Вы  затратите  на  то,  чтобы  втереться  в  доверие  в
какой-либо конторе, имеющей персональные ЭВМ.
   ПЕРСОНАЛЬНЫЕ ЭВМ  названы  так,  потому  что  в  конкретный  момент
времени все ее ресурсы находятся в распоряжении одного оператора.  Она
умещается на столе и имеет,  как минимум,  системный блок, клавиатуру,
дисплей, дисковод,  может быть,  принтер (ДВК-3), а также жесткий диск
типа винчестер   и  мышь  (IBM  PC).  ДВК-3  -  это  для  пользователя
практически то же самое,  что и СМ-1420;  но  делать  на  ней  большие
программы в отсутствии жесткого диска невозможно.
   Стандартная конфигурация IBM PC (из прайс-листов их продавцов):
   - AT-286/287, 16, 1х40, VGA - означает, что эта ЭВМ имеет процессор
Intel-80286,  математический  сопроцессор  Intel-80287  для  плавающей
арифметики,  тактовую  частоту  16МГц  (одинаковые расчетные программы
выполняет в 4-8 раз быстрее, чем ДВК), 1М ОЗУ и 40М винчестер, дисплей
типа VGA;
   - AT-386/387, 33, 4x120, SVGA - процессор Intel-80386 с сопроцессо-
ром, 33МГц,  4М ОЗУ,  120М винчестер и дисплей SuperVGA,  отличающийся
большим разрешением  в  графике.  Это  минимальная  конфигурация,  при
которой  можно  нормально работать с современной операционной системой
MS-Windows, а также в  системе  программирования  Borland  Pascal  7.0
(которая на 286 машине ни в память не уместится, ни на диск).
   Вообще, одинаковые программы на IBM PC как минимум в 3 раза больше,
чем на  СМ  или  ДВК.  Это  объясняется  как более громоздкой системой
команд процессора,  так и (иногда) менее  эффективными  трансляторами.
Тем не  менее,  фирма  IBM  настолько  лидирует  на рынке персональных
компьютеров, что является почти монополистом.  Теперь  же  на  IBM  PC
разработано столько ППО, к которому все привыкли, что переход на более
перспективную вычислительную технику вообще проблематичен.
   Следует заметить, что на Западе IBM PC не считается серьезной ЭВМ и
используется в основном в малом и среднем бизнесе,  как рабочее  место
секретаря и  для компьютерных игр.  Для серьезной научной и инженерной
работы используются персональные ЭВМ Apple  Macintosh,  которые  имеют
гораздо большие ресурсы и стоят в несколько раз больше, чем IBM PC.

                          Системы счисления

   Бит (от  английского  выражения  "binary digit" - "двоичная цифра")
является минимальной единицей  информации.  В  одном  бите  содержится
столько же информации, сколько в ответе на вопрос "да или нет ?". Один
бит информации передается по проводникам ЭВМ в  виде  одного  из  двух
уровней напряжения.  Соответственно, вводятся понятия "логический 0" и
"логическая 1". Почему 0 и 1 ?
   Вы наверняка слышали,  что "ЭВМ работает в двоичной системе счисле-
ния". Давайте вспомним о системах счисления.
   В повседневной жизни мы пользуемся десятичной системой счисления  -
для  записи  чисел достаточно 10 цифр:  0,1,2,3,4,5,6,7,8,9.  Наряду с
десятичной в программировании широко применяются двоичная,  восьмерич-
ная и шестнадцатеричная системы счисления.
   Основание десятичной   системы  счисления  равно  10.  Для  примера
возьмем число 847 (в десятичной системе счисления).  Это число из трех
цифр можно  представить  в  виде  суммы трех произведений (^ обозначим
возведение в степень):
            8*10^2 + 4*10^1 + 7*10^0  = 800 + 40 + 7 = 847
   Каждое произведение получается умножением соответствующей  цифры  в
записи числа на основание системы счисления (в данном случае число 10)
в степени,  равной номеру позиции цифры.  Например, цифра "7" стоит на
нулевой позиции числа 847, тогда ей (цифре "7") соответствует произве-
дение числа 7 на 10 в степени 0. Напомним, что любое число в степени 0
равно 1. Цифре "4" соответствует произведение числа 4 на 10 (10 в сте-
пени 1), а цифре 8 - произведение числа 8 на 100 (10 в степени 2). Это
правило применяется при переводе чисел из какой-либо системы счисления
в понятную нам десятичную систему.  Позиции цифр (номера  разрядов)  в
записи числа нумеруются от 0 и далее справа налево.
   Для записи чисел в двоичной системе счисления используются 2 цифры:
0 и 1. Попробуем перевести двоичное число 1001 в десятичное, учитывая,
что  основание  системы  счисления  равно 2.  По аналогии с предыдущим
примером  десятичное  значение  двоичного  числа   1001   определяется
сложением произведений:
      1*2^3 + 0*2^2  + 0*2^1 + 1*2^0 = 1*8 + 0*4 + 0*2 + 1*1 = 9
   Аналогично переводятся  числа  из  восьмеричной системы счисления в
десятичную.  Например,  восьмеричное число 377  в  десятичной  системе
будет равно сумме:
            3*8^2 + 7*8^1 + 7*8^0 = 3*64 + 7*8 + 7*1 = 255
   Для представления  чисел  в  шестнадцатеричной  системе   счисления
используются    16    цифр:   0,1,2,3,4,5,6,7,8,9,А,В,С,D,E,F.   Буквы
A,B,C,D,E,F  в данном случае  надо  рассматривать  как  цифры,  равные
соответственно  10,11,12,13,14,15.
   Если записывать число в двоичной системе счисления,  один бит будет
соответствовать одному   двоичному   разряду   числа.   Действительно,
двоичная  система наиболее удобна для вычислительной техники,  так как
проще сделать  устройства  для  распознавания  всего  двух  логических
уровней -  0  и 1.  Поскольку бит является слишком мелкой единицей,  в
вычислительной  технике  используют  понятия: 1 байт = 8 бит, килобайт
(1К = 1024 байт),  мегабайт (1М =  1024К).

                    Представление информации в ЭВМ

   Информация в   ЭВМ   в   конечном   итоге   представляется  битами.
Соответственно, любая информация перед  использованием  в  ЭВМ  должна
быть каким-либо   образом   закодирована.  Закодированную  информацию,
которую может  обрабатывать  процессор   определенными   порциями,   в
программировании называют данными.
   Основными данными,  с  которыми может работать процессор,  являются
байт и машинное слово (размер машинного слова кратен  размеру  байта).
Как правило,  в  слове  может быть размещено целое число,  а в байте -
один символ текста.
   Рассмотрим 16-разрядную ЭВМ (например,  СМ-4,  ДВК или БК). Разряды
машинного слова нумеруются справа налево от 0 до 15. При этом разряд с
номером 0 (нулевой разряд) называется младшим, а разряд с номером 15 -
старшим.  Такой способ  записи  слова  совпадает  с  записью  числа  в
двоичной  системе  счисления.  При этом старший разряд слова считается
знаковым.  Он  равен  "1"  для  отрицательных  чисел  и  "0"   -   для
положительных.
   Из 16 бит можно составить 2^16 = 65536 различных  комбинаций.  Если
учесть,  что  машинные команды работы с целыми числами считают старший
разряд знаковым, то диапазон целых чисел будет от -32768 до +32767:

     ────────────────────┬───────────────────────────────────────
                         │          десятичное значение
        двоичное число   ├───────────────────┬───────────────────
                         │  с учетом знака   │  без учета знака
     ────────────────────┼───────────────────┼───────────────────
        0000000000000000 │           0       │           0
        0000000000000001 │           1       │           1
        0000000000000010 │           2       │           2
        ...              │                   │
        0111111111111110 │       32766       │       32766
        0111111111111111 │       32767       │       32767
        1000000000000000 │      -32768       │       32768
        1000000000000001 │      -32767       │       32769
        1000000000000010 │      -32766       │       32770
        ...              │                   │
        1111111111111110 │          -2       │       65534
        1111111111111111 │          -1       │       65535
     ────────────────────┴───────────────────┴───────────────────

   Беззнаковые целые  числа  используются  для  обозначения  адресов в
памяти ЭВМ,  а  имеющие  знак  -  при   целочисленных   арифметических
вычислениях. Следует иметь в виду, что в большинстве ЭВМ отрицательные
числа представляются в дополнительном коде (посмотрите в таблице,  как
они выглядят и найдите закономерность - правило их образования).  Если
мы возьмем целое число и будем все время  увеличивать  его  на  1,  то
произойдет переполнение:  +32767 превратится в -32768. Как правило, ни
один язык программирования не проверяет такую ситуацию,  и  Вы  можете
легко допустить ошибку, если не будете помнить о допустимом диапазоне.
   Для того,  чтобы  представить символы в памяти ЭВМ,  их кодируют по
стандартным таблицам (берут оттуда номер соответствующего символа).  В
одном байте  -  8 бит,  что позволяет закодировать 2^8 = 256 различных
символов.  Этого  вполне  достаточно,  чтобы  разместить  строчные   и
заглавные буквы двух алфавитов,  цифры и другие спецзнаки.  Именно для
удобства кодировки символов  и  был  выбран  такой  размер  байта  как
единицы памяти ЭВМ.
   Для кодировки символов  применяется  международный  стандарт  ASCII
(American   Standard  Code  for  Information  Interchange).  Некоторые
национальности испытывают затруднения - не учтенные в  этом  стандарте
символы  национальных  алфавитов приходится ставить на место греческих
букв и других спецзнаков.  В  советских  аналогах  IBM  PC,  как  и  у
японских  Yamaha,  ориентированных  на  наш  рынок,  используется одна
кодировка.  В фирменных IBM PC, расчитанных на наш рынок, используется
совершенно другая кодировка. Де-факто она уже стала основной. Еще одна
отличающаяся кодировка используется в Microsoft Windows.
   Для расчетов  используется вещественная арифметика (числа с плаваю-
щей десятичной точкой).  Формат их представления у разных  процессоров
разный  (от 4 до 10 байт),  а многие процессоры вообще не имеют команд
для их обработки - тогда приходится делать это программным путем.  Для
минимального  вещественного  числа  (4  байта)  диапазон  значений  от
ё1.5e-45 до ё3.4e38 ("e" означает "10  в  степени").  Мантисса  такого
числа содержит около 7-8 верных десятичных цифр.

                          Организация памяти

   У большинства  современных  ЭВМ  память (ОЗУ и ПЗУ) располагается в
едином адресном пространстве.  Возможный размер адресного пространства
определяется   разрядностью   шины  адреса  ЭВМ.  У  ДВК  шина  адреса
16-разрядная,   что   позволяет   адресовать   65536   единиц   памяти
(минимальной  адресуемой  единицей  памяти является байт - стало быть,
адресное пространство ДВК - 64К).  У IBM PC - 20 разрядов, что дает 1М
доступной  памяти.  Это  вовсе  не  означает,  что у каждой ЭВМ памяти
именно столько.  Обычно меньше.  Более того,  память не  обязана  быть
непрерывной. С такого-то по такой адрес может располагаться ОЗУ, далее
может не быть ничего,  в старших  адресах  обычно  располагают  ПЗУ  с
системными   программами   и  регистры  управления  и  данных  внешних
устройств.  В бытовых и персональных компьютерах  в  том  же  адресном
пространстве  может  находится  видеопамять,  каждый  бит которой (или
группа битов) задают на экране дисплея яркость или цвет  одной  точки.
Так формируется графическое изображение на дисплее.
   Знать конфигурацию памяти необходимо при программировании на языках
низкого уровня (ассемблерах), поскольку там фигурируют реальные адреса
и регистры.   Языки   высокого   уровня  прячут  от  программиста  эти
особенности конкретных ЭВМ.

                         Функционирование ЭВМ

   В процессоре есть специальный регистр памяти - счетчик  команд.  Он
всегда содержит  адрес  следующей команды программы,  которую он будет
выполнять. Цикл работы процессора включает:
   - загрузку  во внутренние регистры очередной команды из ОЗУ или ПЗУ
(куда показывает счетчик команд);  команда  представляется  одним  или
несколькими байтами;
   - распознавание кода команды;
   - при  необходимости загрузка в регистры арифметического устройства
операндов данной команды (например, слагаемых для команды сложения);
   - увеличение счетчика команд для установки его на следующую команду
программы;
   - выполнение команды.
   Очевидно, счетчик команд при включении питания ЭВМ  должен  принять
какое-то начальное  значение.  В  нем  аппаратно устанавливается адрес
программы-загрузчика, записанной в ПЗУ.  Загрузчик загружает в ЭВМ  ее
операционную систему  (копирует  ее  программы  с  диска  или  другого
внешнего устройства в ОЗУ) и начинает ее выполнение.  Так что имейте в
виду - любой стоящий перед Вами компьютер все время работает, исполняя
соответствующие участки операционной  системы,  даже  если  ничего  на
экране не  происходит.  Эта  неутомимая  работа  прекращается только с
выключением питания.
   Довольно часто из-за ошибок программиста или из-за сбоев и помех  в
электрических цепях ЭВМ в один прекрасный момент либо портится счетчик
команд,  либо по указанному в нем адресу ОЗУ  оказывается  не  команда
программы,  а абстрактная информация. Тогда в лучшем случае происходит
зависание ЭВМ - в конце концов она  начинает  крутиться  в  каких-либо
областях  данных,  воспринимая  их  как команды программы;  а в худшем
случае может отправиться на программу  операционной  системы,  которая
ведает записью на диск.  Поэтому после зависания ЭВМ следует, загрузив
заново операционную систему,  проверить,  не испортилась ли информация
на диске (на IBM PC для этого используйте Norton Disk Doctor - NDD).

         Машинные команды, ассемблеры и языки высокого уровня

   Рассмотрим простую задачу:  вычислим  сумму  переменных  A  и  B  и
запомним результат в переменной C. На языках высокого уровня мы должны
описать указанные переменные,  а в программе  написать  оператор  вида
C:=A+B. Если  бы  эту  задачу  надо  было запрограммировать в машинных
кодах, она бы сильно усложнилась (напишем этот пример  для  DEC-овской
машины - СМ-1420, ДВК или БК-0010; подробнее см. [8]):
   - разместим  переменную  A  по  адресу  2000   (все   числа   здесь
восьмеричные);
   - разместим переменную B по адресу 2002;
   - разместим переменную C по адресу 2004;
   - разместим программу с адреса 1000:

       013700, 002000, 063700, 002002, 010037, 002004, 000000.

   Разумеется, ни один нормальный человек не пишет  программ  прямо  в
машинных кодах.    Почти    сразу    же    для    ЭВМ   были   сделаны
программы-переводчики (ассемблеры). Они позволяют программисту:
   - описывать  команды  процессора  не  в  цифровой  форме,  а в виде
удобных для запоминания мнемоник (сокращений их английских названий);
   - описывать  некоторые  адреса  памяти  именами  (метками) и при их
упоминании в тексте программы автоматически ставить в  команду  нужные
адреса и смещения.
   Приведенный пример на ассемблере MACRO выглядит так:

   .title  example     ;заголовок программы
   start:  mov   a,r0  ;переслать А в рабочий регистр процессора
           add   b,r0  ;прибавить к нему переменную B
           mov   r0,c  ;переслать результат в С
           halt        ;остановить процессор - конец программы
   a:      .word 5     ;отвести место в памяти и задать
   b:      .word 3     ;начальные значения переменным
   c:      .word 0     ;
   .end    start       ;указать стартовый адрес программы

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


                4. ПРИКЛАДНОЕ ПРОГРАММИРОВАНИЕ (4 ч)

            Этапы решения решения задачи с применением ЭВМ

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

   2) Выбор   прикладного  программного  обеспечения  (ППО).  Если  мы
определили,  что задача должна решаться с применением ЭВМ,  то  первым
делом  надо  посмотреть,  а  нет  ли  уже готовых программ для решения
аналогичных задач. Если нет, то придется поработать самим.

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

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

   5) Если  мы  оказались здесь,  то,  видимо,  все же придется писать
программу самостоятельно.  Прежде всего  нам  предстоит...  выбор  ЭВМ
(если, конечно, есть из чего выбирать). При этом учитываем:
   - доступность ЭВМ;
   - степень нашего знакомства с этой ЭВМ;
   - ее технические характеристики и их сравнение с требованиями нашей
задачи.
   Последнее предполагает оценку как быстродействия (неразумно считать
на калькуляторе большие расчетные задачи, а на большой ЭВМ - расчет по
паре-другой формул),  так и возможностей периферии ЭВМ (если нам нужно
получить  красивые  графики,  надо  иметь графический принтер и т.п.).
Здесь, как никогда, важно умение найти золотую середину. Можно выбрать
очень  мощную  ЭВМ,  которая  за  секунду  решит  задачу,  но при этом
потребуется два месяца на ее подготовку и на  ожидание  в  очереди.  В
этом  случае лучшим вариантом будет взять калькулятор и решить задачу,
потратив полчаса на программирование и два часа  на  расчет  (реальный
случай из моей студенческой практики).

   6) Выбор языка программирования. Здесь определяющим может оказаться
принцип "лучше  то,  что  хорошо  знаешь".  Инженеру  неплохо  было бы
владеть хотя  бы  несколькими  разными  языками  и  ПОПО.
   Для решения большинства задач на IBM PC очень удобен  Turbo  Pascal
(особенно Borland Pascal 7.0).  Особо крутые программисты могут отдать
предпочтение Turbo  C++  (Borland  C++).  FORTRAN  на  IBM  PC  -  это
малоэффективный и неудобный архаизм.
   Другое дело на СМ-1420.  Короткий PASCAL/RT-11 плохо  пригоден  для
научных  расчетов (вспомним хотя бы отсутствие возведения в степень) и
изобилует ошибками;  в то время как FORTRAN/RT-11 - это вылизанная  до
блеска  конфетка,  надежная и поразительно быстрая,  к тому же еще и с
числами двойной точности, и с комплексными числами.
   Особо скажем  о BASIC'e.  При всей моей к нему неприязни,  в случае
необходимости написать быстро маленькую программу  (проверить  решение
студента, например)  я  выбираю  BASIC,  поскольку тут будет затрачено
меньше времени на отладку программы и получение результата.

   7) Составление  спецификаций  к  программе  -  этот сухой термин из
жизни замученных во время застоя конструкторских бюро означает:
   - конкретно определить тип, способ и порядок ввода исходных данных;
   - конкретно  определить  условия  работы  программы,  требования  к
аппаратной части ЭВМ;
   - конкретно определить вид, способ и порядок вывода результата.

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

   9) Теперь  можно  приступить к выбору структур данных и составлению
алгоритма.  Тщательно выполненный, этот этап позволит сэкономить много
времени  при  написании  и отладке программы и увеличит ее надежность.
Могу привести пример из своего опыта.  В одном  случае  неделю  заняло
рисование  алгоритма  -  продумывались  все  мелочи,  день - 800 строк
ассемблерного текста и 3 дня - отладка.  В другом случае, не решившись
на такой подвиг (поскольку я, к сожалению, отношусь к классу свободных
художников), аналогичный участок программы писал всего два дня - и три
месяца ушло на отладку. До сих пор не уверен, что все в порядке.
   Для тех,  кто  не  любит  рисовать  квадратики  на  бумаге,   можно
предложить очень  действенный  способ:  вначале  пишутся комментарии к
программе. Пишутся  все  подробнее  и  подробнее,  образуя   алгоритм,
записанный словами.  Потом  останется  только вручную протранслировать
этот алгоритм на любой язык программирования.

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

   11) Отладка - имеет целью заставить написанную программу  работать.
Состоит из    исправления    синтаксических    (что   элементарно)   и
алгоритмических ошибок (что при плохо  выполненных  предыдущих  этапах
очень мрачно).   Для  отладки  используются  простые  входные  данные,
результат для которых известен.

   12) Тестирование.  Об этом этапе классический советский программист
имеет самое смутное представление.  Он предъявит заказчику  специально
подобранный тестовый  пример  и  будет  убеждать  его в качестве своей
программы. Когда же заказчик купит программу и обнаружит,  что  а)  на
его машине  программа  вообще  не  запускается,  б)  виснет при каждом
удобном случае,  в)  явно  неправильно  работает  на  исходных  данных
заказчика, г) программист из фирмы давно уволился,  а фирма прекратила
существование, и некому предъявлять рекламации...
   Итак, если  задача  отладки  была в том,  чтобы заставить программу
работать правильно,  то на этапе тестирования нужно загнать ее во  все
мыслимые и  немыслимые  режимы  и  ситуации,  только  бы  заставить ее
работать неправильно -  и  выловить  соответствующую  ошибку.  Кстати,
всегда надо быть морально готовым вернуться на этап 7 и даже на 1 !
   В солидных   фирмах,   занимающихся    производством    программных
продуктов, предусмотрен  даже  специальный  порядок - после того,  как
закончится отладка, начинается:
   - альфа-тестирование  экспертами  фирмы,  не принимавшими участия в
проекте;
   - бета-тестирование  - подготовленный к продаже продукт рассылается
известным и авторитетным экспертам-программистам  (я  лично  знаком  с
двумя бета-тестерами     российского     отделения    фирмы    Borland
International, один из которых  только  что  закончил  школу,  но  уже
зарекомендовал себя  перед  лицом  фирмы).  Бета-тестеры,  разумеется,
обязаны не  разглашать  сведений  о  своей  работе  и  о   проверяемом
продукте, ибо они составляют коммерческую тайну;
   - после получения обстоятельных  отзывов  бета-тестеров  проводится
еще раз гамма-тестирование внутри фирмы;
   - только после этого продукт появляется на рынке.
   Из вышесказанного  вытекает  вполне  конкретный практический совет:
поскольку наша  страна  практически   вся   работает   на   ворованном
программном продукте, не соблюдая никаких законов об авторских правах,
хотя бы  обращайте  внимание  на  заголовки  программ  -  если  в  них
присутствуют слова beta или,  не дай бог, alpha - не пользуйтесь этими
программами, они еще не доделаны.

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

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

   Можно рекомендовать следующие методы повышения надежности программ:

   а) соблюдение эргономических требований к своей работе:
   - использование комментариев;
   - рациональное форматирование текста программы;
   - понятные и мнемоничные названия  переменных;
   - соблюдение  некоторых правил именования переменных (скажем,  на Т
начинаются названия типов,  на Р - указатели на соответствующие  типы;
рабочие переменные-индексы  называем  всегда i,j,k;  n всегда означает
количество и т.п.);
   - использование стандартных алгоритмических конструкций;
   - единообразие всех используемых приемов программирования.
   Перечисленные требования    упростят   разработку   и   последующую
модификацию программы как Вам, так и Вашим коллегам;

   б) требования к программе:
   - избегать   нечетких  областей  языка  программирования  и  ошибок
конкретного транслятора (а они встречаются довольно часто);
   - по  возможности  не  использовать  особенностей  конкретных ЭВМ и
знание системных фокусов,  ибо заказчик может поставить  программу  на
чуть-чуть другую  машину,  или  на  другую  конфигурацию  операционной
системы;
   - программа  должна  обрабатывать  все  возможные  ошибки при своей
работе, будь то неправильные действия оператора или  сбой  при  чтении
файла с дискеты;
   - обеспечение   высокого   уровня  сервиса  (см.  (8)  "составление
сценария");
   - аккуратно работать с файлами,  не держать их  долго  открытыми  и
вовремя закрывать  во  избежание  потерь информации даже при случайном
отключении питания ЭВМ;

   в) аккуратное ведение документации как на этапе разработки,  так  и
при описании готового программного продукта;

   г) тщательная отладка и тестирование;

   д) активная работа с заказчиком (обратная  связь  при  эксплуатации
программы).

                 Основные концепции программирования

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

   1. Аналоговое моделирование. Первыми ЭВМ были аналоговые устройства
- т.е.  устройства,  работающие с аналоговым (непрерывным) сигналом, в
отличие от  цифровых  (дискретных).  На аналоговых ЭВМ непосредственно
моделировались и решались большие системы дифференциальных  уравнений.
Это  была  специфическая  область,  практически не имеющая отношения к
тому,  что  сейчас  понимается  под  программированием.   Использовали
аналоговую технику   в  основном  математики,  физики  и  инженеры,  и
просуществовала она   довольно   долго,   пока   быстро   улучшающиеся
характеристики  цифровых  ЭВМ  не  позволили  столь  же успешно решать
аналогичные задачи.

   2. Хаотическое программирование.  Возникло одновременно с цифровыми
ЭВМ и,  видимо,  не   умрет   никогда.   Характеризуется   отсутствием
каких-либо концепций и идеологий. Вначале оно было уделом узкого круга
инженерной элиты в силу ярко выраженной специфичности программирования
и абсолютной недоступности для непосвященных (что,  кстати, породило и
новое направление в научной  фантастике  -  "думающие  машины",  "бунт
машин" и т.п.). Теперь же хаотическое программирование является уделом
новичков, дилетантов и компьютерных маньяков (хаккеров).
   Основные инструментальные средства:  ассемблеры и языки типа BASIC,
FOCAL, FORTRAN.

   3. Структурное  программирование.  Было  предложено  теоретиками  -
классиками и основоположниками  программирования  довольно  давно,  но
стало возможным  только  после  создания  трансляторов языков высокого
уровня, позволяющих    программировать    на     уровне     простейших
алгоритмических конструкций.  Характеризуется систематизацией основных
алгоритмических конструкций   и   их   четким    и    последовательным
использованием при      программировании.     Девизом     структурного
программирования можно считать название  книги  Н.Вирта  "Алгоритмы  +
структуры данных  = программы" - оно указывает основные предметы,  над
которыми должен думать и работать программист.
   Основные инструментальные  средства:  универсальные  языки высокого
уровня типа ALGOL, PL/I, PASCAL, C.

   4. Процедурное    программирование.    Исторически    вытекает   из
структурного программирования,  более того,  является его  необходимой
частью. Однако   я  специально  выделяю  это  понятие,  поскольку  оно
одновременно открывает    более     высокий     понятийный     уровень
программирования. Раньше   речь   шла   о  реализации  алгоритмических
конструкций и в конечном  итоге  -  о  написании  небольших  программ,
решающих конкретные прикладные задачи. На следующем уровне речь идет о
больших программах  и   программных   комплексах.   Здесь   появляется
идеология программирования,  которая  определяет  задачи,  средства  и
порядок разработки программы.
   Основой процедурного    программирования     является     алгоритм,
подвергающийся в  процессе  разработки различным степеням детализации.
Основным понятием здесь является процедура (подпрограмма)  -  отдельно
выполненный  и отлаженный участок алгоритма,  кирпичик,  из которых на
соответствующем иерархическом уровне складывается полный алгоритм.
   Программист, не владеющий процедурным программированием, затыкается
при ведении сложного проекта - ибо сложность общей задачи  многократно
превышает возможности  человека.  Единственным  выходом  будет решение
задачи по кусочкам,  что после  многочисленных  проб  и  ошибок  может
привести к правильному использованию процедурного программирования.  А
может и не  привести  -  и  итог  будет  печальным  (в  лучшем  случае
программиста уволят,    а    в    худшем   -   рождаются   практически
неработоспособные из-за  множества   ошибок   монстры   вроде   старых
операционных систем больших ЭВМ).
   Языки типа PASCAL, C  поощряют процедурное программирование, но при
детальном рассмотрении  оказываются  не  совсем  для  этого  удобными.
FORTRAN на каждом шагу вставляет палки в колеса;  а BASIC  вообще  для
этого не предназначен.

   5. Логическое   программирование.   Типичным   инструментом   здесь
является язык PROLOG.  Здесь программист вообще не использует  понятие
"алгоритм". Он  оперирует  утверждениями  (предикатами)  и логическими
операциями над   ними.   Программирование   сводится   к    тщательной
формулировке различных  сведений о решаемой задаче и получению решения
путем формального  вывода  из  них  ответа  на  поставленный   вопрос.
Единственная пока  область  применения  логического программирования -
это экспертные  системы  и   упражнения   в   области   искусственного
интеллекта. Возможно,  это  тупиковая ветвь программирования;  но есть
и вероятность его широкого применения в  будущем  (скорее  всего,  это
будет возможно    при   возникновении   каких-либо   новых   концепций
программирования).

   6. Объектно-ориентированное  программирование  (ООП).  Возникло оно
довольно   давно   (типичным   объектно-ориентированным   языком   был
достаточно старый  SMALLTALK)  и  использовалось  в основном для задач
моделирования. Сейчас эта парадигма уверенно овладела широкими массами
процедурных  программистов  (после  появления объектно-ориентированных
расширений языков С++ и Turbo Pascal старше 5.5 версии).
   Если базой  процедурного   программирования   являются   алгоритмы,
обрабатывающие данные,  то  ООП  дает  новую,  более  высокую  ступень
абстракции  -  объект.  В  самом  общем  понятии  объект  - это модель
какого-либо предмета или явления  окружающего  мира,  реализованная  в
программе. Объект может иметь внутреннюю структуру (данные) и средства
общения с внешним миром  (процедуры,  за  которыми  закрепился  термин
"методы").   Для   программиста,   прошедшего   школу  структурного  и
процедурного  программирования,  описание   конкретного   объекта   не
вызывает трудностей. Основная задача, которую он решает, это выделение
объектов в решаемой задаче и организация взаимодействия объектов между
собой.   Например,  в  языке  С++  можно  описать  класс  "матрица"  и
определить  возможные   операции   над   объектами   данного   класса.
Разумеется,  при использовании уже описанных объектов программирование
сложных задач существенно упрощается.  Дополнительные возможности дают
механизмы наследования свойств объектов.
   Очень удобно  реализуются  в  этой  парадигме  сложные   диалоговые
программы, в  том  числе  многооконные.  В  них каждый объект из числа
сваленных на дисплее в одну кучу живет самостоятельной жизнью, и общий
диспетчер занимается распределением событий между объектами.  В рамках
чисто процедурного программирования создать такие программные  системы
невероятно сложно;  более  того,  любая  такая  попытка в 99%  случаев
приводит программиста  к  интуитивному  оформлению  своих   мыслей   в
объектной форме,  и он начинает самостоятельно реализовывать концепцию
объектов теми языковыми средствами, которые ему доступны.
   Пока программистская  мысль  идет по пути создания удобных в работе
универсальных объектно-ориентированных сред (например, система WINDOWS
с постоянно  пополняемыми  "магазинами ресурсов",  из которых,  как из
кирпичиков, можно складывать диалоговые программы).  На ЭВМ класса IBM
PC в  области ООП лидируют фирмы Microsoft (WINDOWS и другие продукты)
и Borland International, inc (языковая поддержка ООП - Borland C++ 3.1
и Borland Pascal 7.0; генератор диалоговых форм Object Vision и другие
продукты).

   Из вышесказанного вовсе не следует делать категорические  выводы  о
качестве того или иного языка (и даже программиста). Нельзя, например,
сказать, что FORTRAN безусловно плох потому,  что не поддерживает ООП;
или что   С++  безусловно  хорош.  Во-первых,  объектно-ориентированно
программировать можно и на FORTRAN'е,  и даже на ассемблере (но не  на
BASIC'е !).  Во-вторых,  для  каждой  задачи  - свой инструмент и своя
идеология. Например,  для  простых   расчетных   задач   ООП   слишком
громоздко, сложно и неудобно.
   Если процедурное программирование - это велосипед,  то  ООП  -  это
автомобиль  (в  то  время  как  хаотическое программирование по той же
аналогии - это заблудившийся пешеход).  У каждого из них свои  области
применения.
   Не исключено также,  что достаточно скоро появится  и  самолет;  но
предугадать сейчас направление поиска новой концепции программирования
- дело почти безнадежное.


                5. ЯЗЫК ПРОГРАММИРОВАНИЯ PASCAL (4 ч)

   Автором языка  программирования  PASCAL  (1970)  является профессор
Никлаус  Вирт,  директор  института  информатики  Швейцарской   высшей
политехнической школы.  Кроме  PASCAL,  он  разработал также целый ряд
языков программирования, например, MODULA-2; но PASCAL занимает особое
место. При его проектировании преследовались две основных цели:
   - создать   язык,   удобный   для   обучения  программированию  как
систематической дисциплине, основанный на ряде фундаментальных понятий
структурного и процедурного программирования;
   - осуществить   надежную   и   эффективную   реализацию   языка  на
существующих ЭВМ.
   Как произведение одного автора,  PASCAL отличается завершенностью и
концептуальной однородностью.  В нем  присутствуют  как  все  основные
алгоритмические конструкции         классического         структурного
программирования, так и  все  структурные  типы  данных,  предложенные
К.Хоаром: простые   переменные,   массивы,   последовательные   файлы,
множества, записи и указатели, а также средства построения новых типов
данных. Разработаны   даже   программы  аналитического  доказательства
правильности программ на языке PASCAL.
   В настоящее  время  на самой массовой ЭВМ - IBM PC - соревнуются по
популярности два  языка:  PASCAL  и  C.  Наиболее  популярны   системы
программирования на   этих   языках,  созданные  крупнейшей  фирмой  -
производителем программного продукта Borland International,  inc. Судя
по всему,  фирма  не отдает окончательного предпочтения какому-либо из
этих языков.  Более того,  одни и те же  новшества  в  этих  продуктах
появляются почти   одновременно   (в   PASCAL'е   несколько   раньше).
Исторически профессиональные программисты предпочитают C и  C++,  хотя
PASCAL практически позволяет сделать то же самое,  но он более строг и
надежен, что немаловажно при разработке больших программных проектов.
   Следует заметить,  что  Turbo  Pascal  фирмы  Borland International
сильно отличается от  разработки  Н.Вирта  и  от  позже  утвержденного
стандарта языка. Практически во всем этот продукт превосходит стандарт
по возможностям,  типам данных и  удобству  для  программиста.  Он,  в
отличие от стандарта,  имеет большой набор арифметических типов данных
различной точности,  что делает  его  удобным  для  научно-технических
расчетов; имеется    ряд    нестандартных    операторов,    упрощающих
программирование сложных   алгоритмических   конструкций,   а    также
использование   ассемблера   внутри   программы   для   повышения   ее
эффективности;  имеется большая  библиотека  системных  и  графических
процедур;  наконец,  начиная с версии Turbo Pascal 5.5, поддерживается
современная технология объектно-ориентированного программирования.
   Далее будет описываться в основном стандарт языка, с добавлениями и
примечаниями для "короткого" PASCAL/RT-11 на СМ-1420 и Turbo Pascal на
IBM PC.

   Знакомство с   любым   языком   программирования   начинают  с  его
возможностей по представлению данных.  К простым типам данных в  языке
PASCAL относятся:

   - целые (integer), имеющие диапазон от -32768 до +32767;
   - вещественные (real) в  диапазоне  ё1.5e-45 ..  ё3.4e38  для  СМ-4
(4 байта,  7-8  значащих  цифр) и в диапазоне ё2.9e-39 ..  ё1.7e38 для
Turbo Pascal на IBM PC (6 байт, 11-12 значащих цифр);
   - символьные (char) размером 1 байт;
   - логические (boolean),  принимающие два значения -  истина  (true)
или ложь (false);
   - производные от целого типа:  тип-диапазон,  например, [-5..24], и
перечислимый тип,  например, Day=(пн,вт,ср,чт,пт,сб,вс), где пн,вт,...
есть константы;
   - множество    (set   of   {char,byte,boolean})   из   элементов
символьного, байтового или перечислимого типа).
   В Turbo Pascal'е существуют также типы:
   - shortint: -128..+127 (1 байт);
   - longint: -2147483648..2147483647 (4 байта);
   - byte: 0..255 (1 байт);
   - word: 0..65535 (2 байта);
   - single: ё1.5e-45..ё3.4e38 (4 байта);
   - double: ё5.0e-324..ё1.7e308 (8 байт, 15-16 значащих цифр);
   - extended: ё3.4e-4932..ё1.1e4932 (10 байт, 19-20 значащих цифр).

   Самый простой  и  необходимый  оператор  любого  языка  -  оператор
присваивания: <переменная>:=<выражение> .  В  выражении  в   качестве
операндов могут использоваться константы, переменные, функции.
   Для арифметических типов данных определены операции: "+", "-", "*".
А   вот   деление   приводит  к  различным  результатам  для  целых  и
вещественных  чисел,  поэтому   язык   даже   предусматривает   разные
обозначения: "/" для вещественных и "div" для целых.  Результат целого
деления - тоже целый, дробная часть отбрасывается. Предусмотрена также
операция остатка   от   деления   (для   целых)  "mod".  При  описании
стандартных функций используем обозначения "r" для  вещественных,  "i"
для целых, "c" для символьных и "b" для логических:
   с:=chr(i)     - преобразование кода в символ;
   i:=ord(c)     - преобразование символа (или перечислимого типа)
                   в код;
   i:=pred(i)    - предыдущее по порядку значение (для целых = i-1);
   i:=succ(i)    - последующее по порядку (для целых = i+1);
   b:=odd(i)     - проверяет нечетность;
   i:=round(r)   - округление;
   i:=trunc(r)   - выделение целой части числа (trunc(-3.14)=-3!);
   i:=r          - при простом присваивании отбрасывается дробная
                   часть (при r=-3.14 i=-4);
   r:=int(r)     - выделение целой части;
   i:=Abs(i)  r:=Abs(r) - модуль;
   r:=cos(r)
   r:=sin(r)
   r:=arctan(r)
   r:=exp(r)
   r:=Ln(r)
   r:=sqrt(r)           - √r
   r:=sqr(r); i:=sqr(i) - r¤, i¤

   Порядок выполнения операций (приоритет) в выражении такой же, как и
в других языках программирования:
   - вычисление скобок и функций;
   - умножение и деление;
   - сложение и вычитание.
   Типы операндов и операций  над  ними  должны  соответствовать  друг
другу. Нельзя,  например,  написать  "3.14  div 2".  Тип результата (и
всего выражения) должен соответствовать типу переменной,  которому  он
присваивается.
                             a
   ПРИМЕРЫ:        x = sin¤ ───   ─  x:=sqr(sin(a/abs(b)));
                            │b│
     b + c d
 x = ───────   ─  x:=(b+c*d)/(a+b);
      a + b

   Операция деления (особенно вещественного) наиболее сложна  и  долго
выполняется, поэтому ее следует избегать:
   - лучше записать a/(b*c), чем a/b/c;
   - лучше записать x*0.5, чем x/2.

   Для множеств   определены   операции   "+"  -  добавить  элемент  к
множеству (объединение множеств),  "-" - удалить элемент из множества,
"*" -  пересечение  множеств,  а  также  логическая  операция проверки
вхождения элемента в множество: <элемент> in <множество>.

   Язык PASCAL весьма строгий. Он требует, чтобы все объекты - будь то
константы, метки,  переменные,  процедуры  -  были  описаны  до   того
момента, как   использованы   в   тексте   программы.   Программистов,
развращенных вседозволяющим BASIC'ом, это нервирует. Но потом даже они
привыкают и находят такую строгость полезной.  Я,  например,  доведись
сейчас работать на FORTRAN'е,  буду все время бояться случайно сделать
опечатку в  имени  переменной  и три дня потом вылавливать эту ошибку.
PASCAL такого не позволит,  за что ему спасибо.  Итак,  описания  всех
переменных располагаются в начале программы,  например (данный  пример
не образец для программирования, а просто демонстрация возможностей):

   type TDay=(mon,tue,wed,thu,fri,sat,sun);  {перечислимый тип}
   var  Day:TDay;                {переменная типа TDay}
        WorkDays: set of TDay;   {множество элементов типа TDay}
   begin                       {начало программы}
     WorkDays:=[mon..fri];       {заполнение  множества значениями
                                  от mon до fri}
     for Day:=mon to sun do      {цикл по дням недели}
       if Day in WorkDays        {входит ли Day в множество WorkDays}
         then writeln(ord(Day)+1,'-рабочий день')
         else writeln(ord(Day)+1,'-выходной');
   end.                        {конец программы}

   Здесь показано,   как   можно   создавать   новые   типы  данных  и
пользоваться ими наравне со стандартными;  как  описывать  переменные;
как  работать  с  множествами.  Обратите внимание,  что одновременно с
перечислимым  типом  TDay  описывается  и  ряд  констант,  принимающих
значение:  mon=0;  tue=1  и  т.д.  Этой особенностью мы пользуемся при
выводе номера дня недели,  где функция ord возвращает порядковый номер
константы в перечислимом типе.


            6. ОСНОВНЫЕ АЛГОРИТМИЧЕСКИЕ КОНСТРУКЦИИ  (6 ч)

                     Способы описания алгоритмов

   Напомним, что   алгоритм   -   это   последовательность   действий,
выполнение которых приводит к достижению определенного  результата.  В
повседневной жизни  человека  окружает множество различных алгоритмов,
описанных обычными   словами   (кухонные   рецепты,   руководства   по
эксплуатации бытовой  техники  и  т.п.).  В  технике  используются все
возможные способы описания алгоритмов, основными из них являются:
   - текстовый  (записанный  обычным  языком,  с  пронумерованными для
удобства пунктами;
   - графический  -  в  виде стандартизированных изображений элементов
алгоритмов (алгоритмических конструкций);
   - с  помощью  существующих  языков программирования или упрощенных,
наполовину состоящих   из   комментариев,   подобий    языков    (т.н.
псевдокодов).
   Существовал в свое время Центральный фонд  алгоритмов  и  программ,
периодически публикующий  зарегистрированные алгоритмы в виде процедур
на языке ALGOL.  Так же поступают и  специализированные  периодические
издания, хотя  сейчас  использование для этой цели определенного языка
не регламентировано.  Наиболее часто употребляется PASCAL,  как прямой
потомок ALGOL'а,  имеющий  все необходимые средства для описания самых
различных алгоритмов.
   Рассмотрим подробнее  графический  способ описания алгоритмов,  так
как он часто применяется  не  только  в  программировании,  но  и  при
описании самых   различных   технических   устройств   (для  пояснения
принципов их работы, поиска неисправностей и др.).
    ______
   (______)  - такими   овалами   изображаются   терминальные   пункты
               алгоритма (начало и конец)
   ┌──────┐  - прямоугольниками обозначаются элементарные действия
   └──────┘    алгоритма
   ╓──────╖  - а так изображаются обращения к процедурам
   ╙──────╜    (подпрограммам)
     _____   - параллелограммами изображается ввод (иногда и вывод)
   /____ /     данных
   ┌──────┐  - в виде обрывка бумажной ленты принтера изображается
     __  ─┘    вывод данных

   -      +
  ──<    >── - ромбами изображаются разветвления алгоритма (условные
               операторы)
    ──────   - так иногда обозначается модификация параметров
    ──────     (заголовок цикла с параметром)

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

                  Последовательность действий. Метки

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

   label 1; {описание метки (допустимы только целые числа)}
      ...
      goto 1;  {оператор безусловного перехода}
      ...      {пропущенные операторы}
   1: ...      {метка перед тем оператором, куда делается переход}

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

   Условный оператор  реализует   разветвление   алгоритма   по   двум
возможным путям   (веткам)   в   зависимости  от  результата  проверки
заданного условия (если-то-иначе):
                            if <условие>
          +      -             then <оператор 1>              +
      ┌────<усл.>────┐         else <оператор 2>          ┌────< усл.>
┌─────┴────┐    ┌────┴─────┐                        ┌─────┴────┐
│оператор 1│    │оператор 2│        или             │оператор 1│
└─────┬────┘    └────┬─────┘                        └─────┬────┘
      └─────о──────┘           if <условие>             └──────о
                                  then <оператор>                

   В сокращенной  форме   условного   оператора,   если   условие   не
выполняется, программа просто переходит к следующему оператору.
   Что делать,  если надо выполнить не  один  оператор,  а  несколько?
Нужно использовать   "операторные   скобки"  -  begin  end.  Все,  что
заключено внутри них,  будет рассматриваться снаружи как  один  единый
оператор.
   Теперь о правилах формулирования  условия.  Условие  в  общем  виде
представляет собой   логическое  выражение  (кстати,  для  запоминания
логических выражений   с   целью   их    последующего    использования
используются переменные   логического   типа  -  boolean).  Логическое
выражение состоит  из  операндов  (других  логических  выражений   или
переменных) и  логических  действий над ними.  Элементарные логические
выражения образуются при помощи операций  сравнения,  например,  "x>5"
("=" -  равно,  "<>" - не равно,  ">" - больше,  "<" - меньше,  ">=" -
больше или  равно,  "<="  -  меньше  или  равно),  а  также   операции
принадлежности   элемента   множеству  (<элемент>  in  <множество>)  и
логических функций (odd, eof, eoln).
   Логические операции над возможными значениями  операндов  сведем  в
таблицу, где  a  и  b  -  логические  выражения  или переменные,  а их
значения обозначим 0 - ложь (false) и 1 - истина (true):
───┬───┬─────────┬────────┬─────────┬───────
 a │ b │ a and b │ a or b │ a xor b │ not a     Во-первых, обозначения
───┼───┼─────────┼────────┼─────────┼───────    0 и 1 соответствуют
 0 │ 0 │    0    │   0    │    0    │   1       способу представления
 0 │ 1 │    0    │   1    │    1    │   1       логических сигналов в
 1 │ 0 │    0    │   1    │    1    │   0       цифровой электронике;
 1 │ 1 │    1    │   1    │    0    │   0       а во-вторых, отсюда
───┴───┴─────────┴────────┴─────────┴───────    очень хорошо видно,
почему операция И (and) называется по-другому логическим умножением, а
операция ИЛИ (or) - логическим сложением (1+1=не ноль).  Операция  xor
(исключающее ИЛИ, или сложение по модулю 2) используется в процессорах
ЭВМ для организации арифметического сложения (1+1=10, где полученная 1
переносится в старший разряд).
   Приоритет операций  в  логическом выражении аналогичен приоритету в
арифметическом выражении:
   - вычисления в скобках;
   - унарная операция not;
   - and;
   - or и xor;
   - операции отношения (сравнения).
   Последнее стоит пояснить подробнее.  Дело  в  том,  что  сравнивать
между собой можно и логические выражения (правда, только на = или <>),
например: "a and b = c",   где  a,  b  и  c  -  логические  выражения.
Элементарные же   логические   выражения,   образуемые   из   операций
отношения, следует заключать в скобки: "(x>y) and (x>z)".

   ПРИМЕР 1: Найти максимум из двух чисел x, y.

   if x>y then max:=x else max:=y;

   или:

   max:=x;  {начальное значение для искомого максимума}
   if y>max then max:=y;

   Оба способа  с  точки  зрения  эффективности  одинаковы.  В  первом
наличествует лишняя  операция перехода,  а во втором - лишняя операция
копирования (присваивание max:=x).

   ПРИМЕР 2: Найти максимум из трех чисел x, y, z.

   if x>y                                max:=x;
     then if x>z                         if y>max then max:=y;
       then max:=x          или          if z>max then max:=z;
       else max:=z
     else if y>z
       then max:=y
       else max:=z;

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

   ПРИМЕР 3:  Определить,  входит  ли x в интервал [c,d] (для экономии
места ограничимся лишь написанием логического выражения):

   (x>=c)and(x<=d)

   Перепишем это  выражение,  чтобы оно было истинным,  когда точка НЕ
входит в интервал:

   not((x>=c)and(x<=d))    или   (x<c)or(x>d)

   Сделаем выводы:
   - not (x>y) = (x<=y) ,  то есть при отрицании  строгое  неравенство
превращается в нестрогое и наоборот;
   - проанализировав  этот  простой  пример,  очень   легко   получить
уравнения,  используемые  для  преобразования  и  упрощения логических
выражений (правила де Моргана):

   not (a and b) =  (not a)  or  (not b)
   not (a or  b) =  (not a) and  (not b)


                         Оператор выбора CASE

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

     case <управляющее выражение> of
       <список констант>: <оператор>;
       ...
       <список констант>: <оператор>;
       else <оператор>
     end;

   Управляющее выражение  может  быть   любого   перечислимого   типа.
Напомним,  что  вещественный  тип  данных перечислимым не является,  и
операция точного  сравнения  вещественных  данных   в   общем   случае
некорректна. Поэтому вещественные данные в операторе CASE использовать
нельзя.
   Список констант  -  это  одна  или несколько констант,  разделенных
запятыми;  если значение управляющего выражения совпадает с  одной  из
констант,  то  выполняется  соответствующий  оператор.  Все  приличные
трансляторы  PASCAL'я  допускают  использование  конструкции  ELSE   -
соответствующий  оператор  выполняется  в  том  случае,  если значение
управляющего выражения  не  совпадает  ни  с  одной  из  перечисленных
констант. Если конструкция ELSE отсутствует,  то ни один из операторов
может не выполниться.  Не  могут  также  быть  выполнены  одновременно
несколько  операторов  (соответственно,  одинаковые  константы  в CASE
использовать нельзя).  Имейте в виду,  что оператор  CASE  в  PASCAL'е
реализует  следующий  вид  разветвлений  (в других языках может быть и
по-другому):

             ┌──┬──┴──┬───┐                   ┌──┬──┴──┬──┐
            ┌┴┐┌┴┐   ┌┴┐┌─┴──┐               ┌┴┐┌┴┐   ┌┴┐ │
            │1││2│...│n││else│     или       │1││2│...│n│ │
            └┬┘└┬┘   └┬┘└─┬──┘               └┬┘└┬┘   └┬┘ │
             └──┴──┬──┴───┘                   └──┴──┬──┴──┘

   Удобство оператора CASE для программиста проиллюстрируем следующими
примерами:

   { ПРИМЕР 1 }
   var Day:integer;
   begin
     write('введите номер дня недели:'); readln(Day);
     case Day of
       1: writeln('понедельник');
       2: writeln('вторник');
       ...
       7: writeln('воскресенье');
       else writeln('такого дня нет!')
     end;
   end;

   { ПРИМЕР 2 }
   var Day: (понедельник, вторник, ... , воскресенье);
   ...
   case Day of
     суббота, воскресенье: writeln('выходной день');
     else                  writeln('рабочий день')
   end;

   Следует заметить,  что трансляторы обычно переводят оператор CASE в
последовательность проверок вида:

   if (Day=суббота) or (Day=воскресенье)
     then writeln('выходной день')
     else writeln('рабочий день')

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

                  Организация циклов в языке PASCAL

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

                          Цикл с параметром
             ───┴───
     ┌───< заголовок >────┐
     │       ───┬───       │
     │    ┌─────┴────┐     │
     └────┤тело цикла│     
          └──────────┘

   Формат оператора:

   for <параметр>:=<нач.значение> to     <кон.значение> do <оператор>
   for <параметр>:=<нач.значение> downto <кон.значение> do <оператор>

   Цикл с  параметром  используется,  когда количество повторений тела
цикла заранее задано. Эта ситуация возникает:
   - при работе с регулярными структурами данных (массивами,  списками
и пр.);
   - при  выполнении  программой  самых различных управляющих действий
(например, напечатать  разделительную  строчку  из  80   знаков   "-";
очистить 5 строк экрана; 3 раза мигнуть картинкой на экране и т.п.);
   - при реализации ряда вычислительных методов (например,  вычисление
определенного интеграла методом прямоугольников).
   Во всех  этих  случаях  недопустимо  пропустить  или   лишний   раз
повторить  тело  цикла  -  количество  повторений  строго  определено.
Поэтому естественным представляется использование в операторе цикла  с
параметром  только  перечислимых  типов  данных.  В  некоторых  языках
ограничения менее жестки. Например, в BASIC'е можно написать цикл

   for x=1 to 3

но  ни  один  программист  не  скажет  с  уверенностью,   сколько  раз
выполнится этот цикл (виной этому вычислительная погрешность:  x может
меняться по-разному в зависимости от языка и машины - 1, 1.999, 2.998,
3.997 или же 1, 2.001, 3.002 - и цикл будет выполняться по-разному).
   Следующее, на  мой  взгляд,  неоправданное,  ограничение  оператора
цикла с  параметром  состоит в том,  что шаг изменения параметра может
быть только +1 (в заголовке цикла пишется  to)  или  -1  (в  заголовке
цикла пишется downto).

   Классическим примером   использования   цикла  является  вычисление
факториала  n! = 1*2*3*...*(n-1)*n :

   var i,n:integer; f:real;
   ...
   f:=1; for i:=2 to n do f:=f*i;

или целой  степени  x^n = x*x*...*x  (n иксов и n-1 умножений):

   var i,n:integer; s,x:real;
   ...
   s:=1; for i:=1 to n do s:=s*x;

   Обратите внимание на начальные значения степени и факториала  и  на
начальные и  конечные  значения  параметров  циклов,  что обеспечивает
нужное количество повторений.  Заметим,  что если  начальное  значение
параметра окажется  больше  конечного,  цикл  не  выполнится ни одного
раза. Так, при n=0 и n=1 факториал останется равным 1, а x^0 = 1.

   Как уже  было  сказано,  в  качестве  параметра  цикла  может  быть
использована переменная  не  только  целого,  но и других перечислимых
типов, например:

   var Day: (понедельник, вторник, ... , воскресенье);
   var b:boolean;
   var c:char;
   ...
   for Day:=среда to пятница do ...
   for b:=false to true do ...
   for c:='z' downto 'q' do ...

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

   var строка,столбец:integer;
   begin
     for строка:=1 to 10 do begin  {печать очередной строки}
       for столбец:=1 to 10 do
         write(строка*столбец:4);  {печать числа в 4 позиции}
       writeln;                    {переход к новой строке}
     end;
   end.

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

                         Цикл с предусловием

   Цикл с   предварительной   проверкой   условия   (с   предусловием)
используется в тех случаях, когда проверку удобнее выполнять до начала
тела цикла:
                           while <условие> do <оператор>
   ┌────── 
   │            -       Эта форма организации цикла имеет особенности:
   │     <  ?  >────┐   - в заголовке проверяется условие
   │     +          │     ПРОДОЛЖЕНИЯ цикла; цикл кончается, когда
   │  ┌─────┴────┐  │     условие перестает выполняться;
   └──┤тело цикла│     - если условие не выполняется с самого начала,
      └──────────┘        тело цикла не исполнится ни разу.

   Посмотрим на примере вычисления  степени,  как  можно  использовать
цикл с предусловием вместо цикла с параметром:

BASIC                       PASCAL                 комментарии

                            var i,n:integer;
10 s=1                      s:=1;
20 i=1;                     i:=1;                  нач.значение
30 if i>n+0.5 then 70       while i<=n do begin    проверка условия
40 s=s*x                      s:=s*x;              тело цикла
50 i=i+1                      i:=i+1;              изменение параметра
60 goto 30                  end;
70 print s                  writeln(s);

   Обратите внимание на необходимые составные части цикла:
   - начальное значение параметра цикла;
   - заголовок цикла (проверка условия продолжения цикла);
   - изменение параметра в теле цикла.

   Сравнивая написанные  рядом  программы  на  BASIC'е  и на PASCAL'е,
можно также сделать некоторые выводы:
   - BASIC,   как   язык   относительно   низкого  уровня,  схематично
показывает, как  будет  выглядеть  программа  на  PASCAL'е  после   ее
трансляции в машинные коды;
   - программы  очень  похожи,  но  стандартная  конструкция  цикла  в
PASCAL'е скрывает команды перехода и поэтому более удобна для анализа;
к тому же при правильном применении гарантирует от возможных ошибок (в
BASIC'е же легко перепутать номера строк в операторах перехода);
   - применение целого параметра цикла в данном  примере  на  PASCAL'е
гарантирует необходимое количество повторений цикла; в то время как на
версиях BASIC'а,   не   имеющих   целого   типа   данных,   приходится
обеспечивать надежность путем проверки условия i>n+0.5.

                         Цикл с постусловием

   Цикл с   проверкой   условия   после   выполнения   тела  цикла  (с
постусловием) также часто бывает удобен:

                           repeat
   ┌────── │                <тело цикла>
   │  ┌─────┴────┐         until <условие>;
   │  │тело цикла│
   │  └─────┬────┘    Эта форма организации цикла имеет особенности:
   │                 - в заголовке проверяется условие
   │    -               ОКОНЧАНИЯ цикла; цикл кончается, когда
   └─────<  ?  >        условие выполнится;
                      - даже если условие выполняется с самого начала,
            +          тело цикла исполнится один раз;
                      - в теле цикла может быть сколько угодно
                        операторов.

   Посмотрим тот же пример для цикла с постусловием:
───────────────────────┬──────────────────────┬───────────────────────
BASIC                  │    PASCAL            │    комментарии
───────────────────────┼──────────────────────┼───────────────────────
                       │    var i,n:integer;  │
10 s=1                 │    s:=1;             │
20 i=1;                │    i:=1;             │   нач.значение
                       │    repeat            │
30 s=s*x               │      s:=s*x;         │   тело цикла
40 i=i+1               │      i:=i+1;         │   изменение параметра
60 if i<=n then 30     │    until i>n;        │   проверка условия
70 print s             │    writeln(s);       │
───────────────────────┴──────────────────────┴───────────────────────
   По сравнению  с  предыдущим  примером  (цикл  с  предусловием)  эта
программа хуже - она  не  позволит  вычислить  x  в  нулевой  степени,
поскольку тело   цикла   выполнится   хотя  бы  один  раз.  Для  учета
возможности n=0  здесь  придется  добавлять  еще  условный   оператор.
Следовательно, в данном случае, цикл с постусловием неудобен.


            Примеры циклов с незаданным числом повторений

                                                1
ПРИМЕР 1. Найти сумму бесконечного ряда  S =    ─  с точностью 0.001.
                                            i=1 i¤
   При решении этой задачи следует вспомнить  математику:  бесконечный
ряд, сумма  которого  конечна,  является  сходящимся.  При  этом сумма
отброшенной части  ряда  не  будет   превышать   величины   последнего
учтенного слагаемого.  Этот  факт  и  используется  в качестве условия
окночания цикла:  цикл суммирования будем продолжать до тех пор,  пока
очередной член  ряда не станет меньше заданной точности.  Это даст нам
основание утверждать, что задача решена и вся сумма найдена с заданной
точностью.

   var s,r,eps:real; i:integer;
   begin
     write('Введите точность:'); readln(eps);
     s:=1; i:=2;          {первый член суммы и так ясен}
     repeat
       r:=1/sqr(i);       {очередной член суммы}
       s:=s+r;            {прибавляем его к сумме}
       i:=i+1;            {изменяем параметр цикла}
     until abs(r)<eps;    {условие выхода из цикла}
     writeln('Сумма=',s);
   end.

   Перепишем основную часть программы, используя цикл с предусловием:

   s:=1; i:=2; r:=2*eps;         Обратите внимание, что теперь
   while abs(r)>eps do begin     необходимо задать начальное значение
     r:=1/sqr(i);                переменной r для того, чтобы условие
     s:=s+r;                     при первой проверке было истинно и
     i:=i+1;                     тело цикла выполнилось.
   end;

   ПРИМЕР 2.  Подсчитать  среднее арифметическое вводимых с клавиатуры
положительных чисел.
   Любая операция  ввода должна быть когда-то закончена.  Часто бывает
необходим специальный признак  окончания  (например,  ситуация  "конец
файла"). В  данном случае мы можем использовать поставленные в условии
задачи ограничения   на   входные   данные    (числа    должны    быть
положительными) и напишем цикл с незаданным числом повторений, который
закончится, как только будет введено отрицательное число:

   var n:integer; x,s:real;
   begin
     n:=0; s:=0;
     repeat
       read(x);                 {читаем очередное число}
       if x>=0 then begin       {считаем сумму}
         s:=s+x;
         n:=n+1;
       end;
     until x<0;
     writeln('среднее арифметическое=',s/n);
   end.

            7. УПОРЯДОЧЕННЫЕ ТИПЫ ДАННЫХ - МАССИВЫ  (2 ч)

   Массив - это упорядоченная  (регулярная)  структура  данных.  Можно
сказать, что  массив  -  это  аналог  табличного способа представления
информации. В отличие от  простых  типов  данных  массив  относится  к
сложным, ибо он состоит не из одного, а из нескольких элементов.
   Массив характеризуется:
   - размерностью (количеством измерений);
   - размерами (диапазонами изменения индексов) по каждой размерности.
   Например, одномерный массив удобно использовать для описания такого
математического объекта,  как вектор;  двумерный массив - это матрица;
трехмерный массив можно использовать для описания состояния каких-либо
объемных объектов.
   Одномерный массив   (вектор)   удобно   также   использовать    для
представления в программе текстовых строк.
   Размерность массивов стандартом PASCAL'я  не  ограничивается.  Если
Вам требуется   описать  двумерную  (плоскую)  таблицу  из  элементов,
являющихся трехмерными массивами, никто не мешает это сделать.

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

   Описание массивов:  var имя: [размер] of тип
   Примеры описания новых типов:

   type
     Vector=array[0..24] of real;             {математика: вектор}
     Matrix=array[1..18,1..20] of integer;    {математика: матрица}
     String=array[1..255] of Char;            {текстовая строка}
     Day=(ПН,ВТ,СР,ЧТ,ПТ,СБ,ВС);
     Days=array[Day] of integer; {перечислимый тип в качестве индекса}
     Power=array[-15..5] of real;{допустимы и отрицательные индексы}

   Как видим,  описание  размеров  массивов  осуществляется  с помощью
перечислимого типа данных либо с помощью типа-диапазона.
   В Turbo   Pascal  имеется  специальный  вид  массива  для  хранения
текстовых строк:  string. Если размер строки не указан, считается, что
это string[255] - строка из 255 символов.

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

   Var Screen: array[1..24,1..80] of Char;
   Screen[11,23]:='*';

или же как массив из текстовых строк:

   Var Screen: array[1..24] of array[1..80] of Char;
   Screen[11][23]:='*';    {присвоение значения элементу массива}
   Screen[2]:='Привет!';   {присвоение значения сразу целой строке}

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

   ПРИМЕР 1.  Вычисление полинома по схеме Горнера.
   Пусть мы имеем полином n-й степени,  коэффициенты которого хранятся
в массиве-векторе:

   const n=10;
   var a: array[0..n] of real;

тогда для заданного значения х вычисление полинома удобно и эффективно
производить по схеме Горнера:

   S(x) = a0 + a1*x + a2*x^2 + ... + an*x^n =
        = a0 + x*(a1 + x*(a2 + ... x*(an-1 + x*an) ... ))

   s:=a[n]; for i:=n-1 downto 0 do s:=a[i]+x*s;

   ПРИМЕР 2.  Ввести  матрицу  размером 5 x 10 и найти ее максимальный
элемент.
   Сначала определим  сценарий диалога:  как будет осуществляться ввод
матрицы с клавиатуры.  Видимо,  наиболее удобным способом  будет  ввод
матрицы по строкам. Позаботимся об удобной подсказке оператору:

   Введите матрицу размером 5х10
   1 строка: 1,2,3,...,10
   2 строка: 1,2,3,...
   ...
   5 строка: ...

и напишем программу, соответствующую составленному сценарию:

   Const m=5; n=10;   {использование констант облегчает возможную
                       модификацию программы}
   var
     a:array[1..m,1..n] of real;
     i,j: integer;
     max: real;
   begin
     {ввод матрицы}
     writeln('Введите матрицу размером ',m,'x',n);
     for i:=1 to m do begin  {цикл по строкам}
       write(i:2,' строка: ');
       for j:=1 to n do read(a[i,j]);
     end;
     {поиск максимума}
     max:=a[1,1];  {начальное значение - первый кандидат на максимум}
     for i:=1 to m do
       for j:=1 to n do
         if a[i,j]>max then max:=a[i,j];
     writeln('Максимум=',max);
   end.

   Стало быть,  максимум мы находим путем сравнения переменной max  со
всеми по  порядку элементами матрицы.  Каково должно быть ее начальное
значение? Если поставить,  к примеру,  ноль, то это плохое решение - в
матрице все элементы могут оказаться отрицательными, и тогда получится
неправильный ответ - ноль.  Можно  попытаться  задать  заведомо  очень
маленькое число  (например,  -1e30).  Но  где  гарантия,  что  числа в
матрице не окажутся еще меньше?  Можно,  конечно,  взять за  начальное
значение самое  маленькое  число,  которое  может  быть представлено в
машине, но:
   - я, например, не знаю, что это за число (его еще надо посчитать);
   - на другой  машине  с  другой  разрядностью  минимально  возможное
число, очевидно, будет другим; а программа всегда должна быть наиболее
универсальной и в наименьшей степени зависеть от машины и от  исходных
данных.
   Следовательно, остается один правильный путь: в качестве начального
значения искомого  максимума  нужно  взять  один из элементов матрицы.
Например, a[1,1].  Иногда для реализации  этого  приема  придется  как
следует подумать;   возможно,   надо   будет  вводить  вспомогательные
переменные (флаги)  и  условные  операторы.   Но   зато   этот   прием
гарантирует стопроцентную логическую правильность алгоритма.

   ПРИМЕР 3.  Ввести строку текста.  Сосчитать количество букв  'A'  и
заменить символы '-' на символы '+'.

   Наиболее существенная  проблема  при  работе  с массивами возникает
из-за их статического характера (невозможности изменения размеров). На
самом же  деле  в  большинстве  реально  встречающихся случаев заранее
неизвестно, какое количество данных будет обработано  программой  -  и
каков должен  быть  размер  массива  для  их хранения.  Яркий пример -
программа редактирования  текстов   (текстовый   редактор)   -   можно
редактировать письмо    из    пары   строк   или   же   целую   книгу,
подготавливаемую к печати с помощью ЭВМ.
   Существует только два способа решения проблемы:
   - наилучший: использование вместо статических массивов динамические
структуры данных  (списки  строк  и  т.п.),  которые  распределяются в
свободной памяти в процессе  выполнения  программы  и  займут  столько
памяти, сколько реально потребуется;
   - половинчатый компромисс: описать достаточно большой массив, чтобы
его хватило   в  80%  случаев  (в  20%,  разумеется,  программа  может
оказаться неработоспособной).

   В этом примере как раз  и  неизвестно,  какой  длины  строка  будет
введена. Поэтому  имеет  смысл  описать  массив  с запасом (в разумных
пределах!). Например,  в 80 символов (ширина экрана). Или уж совсем на
всякий случай  -  255  символов  (максимальная  длина строки на многих
языках программирования, в том числе и на Turbo Pascal).
   Возникает, однако, еще одна проблема: как распознать конец вводимой
строки?  Вообще говоря,  в  PASCAL'е  есть  функция  EoLn,  которая  и
определяет ситуацию  конца строки.  Но в используемом нами трансляторе
PASCAL/RT-11 эта функция при вводе с клавиатуры не работает.  Чтобы не
усложнять себе  жизнь,  придется искусственно придумать способ указать
программе, что строка закончена.  Можно попросить, например, поставить
в конце  строки  точку  (оговоримся  сразу,  что  ставить пользователю
дополнительные ограничения - это верх безобразия  в  программировании;
но данный пример носит чисто иллюстративный характер,  и мы смиримся с
этим):

   var
     n,i,count: integer;
     Line:  array [1..255] of char;
   begin
     writeln('Введите строку, заканчивающуюся точкой:');
     readln; { это  необходимо для PASCAL/RT-11;  подробнее см.
               "Работа с файлами"}
     n:=0;   { начальное значение длины введенной строки }
     repeat  { ввод строки }
       n:=n+1;
       read(Line[n]);
     until Line[n]='.';
     { обработка строки }
     count:=0;  { счетчик букв "А" }
     for i:=1 to n do begin
       if Line[i]='A' then count:=count+1; { считаем буквы "А" }
       if Line[i]='-' then Line[i]:='+';   { заменяем "-" на "+" }
     end;
     { вывод преобразованной строки }
     for i:=1 to n do write(Line[i]);
     writeln;
   end.

   Осталось рассмотреть еще один  важный  вопрос:  когда  же  все-таки
нужно использовать  массивы,  а  когда - нет.  Было бы непростительной
ошибкой автоматически заводить массив при первом упоминании каких-либо
математических формул  с  индексами.  При  работе  с большими объемами
однотипных данных   (первое   показание   к   использованию   массива)
необходимо определить,  сколько  раз  будет  использоваться программой
каждый из элементов  предполагаемого  массива.  Если  все  эти  данные
должны быть   доступны   программе   одновременно,  и  она  много  раз
просматривает их в каком-либо порядке (или даже хаотически), то массив
нужен. Если  каждый элемент после однократного использования больше не
нужен, то и нет смысла тратить память на  его  хранение  -  массив  не
нужен.
   Так что,  если ограничиться рамками конкретно поставленных задач  в
примерах 2  и  3,  то массив там не потребуется.  Например,  отыскание
максимального элемента  матрицы  фактически  сводится  к  однократному
просмотру входного  потока данных (если вводимая матрица больше ни для
чего не потребуется!) - это задача на два  вложенных  цикла  и  ничего
более.  Эти же примеры носили чисто иллюстративный характер.


                 8. ТИПОВЫЕ ЭЛЕМЕНТЫ АЛГОРИТМОВ (2 ч)

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

                       Целочисленная арифметика

   1. Дано целое число. Выделить в нем третью десятичную цифру (разряд
сотен).
   Анекдот: Студента  спросили  - как правильно написать:  "Здесь" или
"Сдесь". Он ответил - напишите "Тут".
   Не меньше    десятка    аналогичных    предложений   поступило   от
затруднявшихся в данной задаче студентов.  А задача решается  всего  в
два действия (причем двумя способами). Пусть исходное число хранится в
переменной n типа integer (для примера пусть n=4321). Тогда:

   i:=n div 100;   {определим количество сотен в числе: i=43}
   i:=i mod 10;    {возьмем младший разряд полученного числа: i=3}

или по-другому:

   i:=n mod 1000;  {i=321}
   i:=n div 100;   {i=3}

   2. Определение четности.
Можно написать:
   if n mod 2 = 0 then write('чет') else write('нечет');
Еще проще использовать стандартную функцию:
   if odd(n) then write('нечет') else write('чет');

                       Вещественная арифметика

   1. Начальные значения.
Сумма:         s:=0; for i:=1 to n do s:=s+f(i);
Произведение:  p:=1; for i:=1 to n do p:=p*f(i);
Поиск максимума    или    минимума    среди    элементов    какой-либо
последовательности: в  качестве  начального  значения   кандидата   на
максимум (минимум) берется первый элемент последовательности.

   2. (-1)^n :  if odd(n) then x:=-1 else x:=1;

   3. Периодическое изменение знака (триггер).
                      n     k   1
Пусть надо вычислить    (-1) * ───
                     k=1       x*k

   s:=0;                    {начальное значение суммы}
   z:=-1;                   {начальное значение триггера}
   for k:=1 to n do begin
     s:=s+z/(x*k);
     z:=-z;                 {смена знака (переключение триггера)}
   end;

Продемонстрированный способ переключения переменной-триггера в  данной
задаче эффективнее, чем вычисление (-1)^k по п.2.

   4. Вещественные циклы с заданным количеством повторений.
Пусть требуется напечатать таблицу значений f(x) для x от  0  до  2  с
шагом 0.2. Можно было бы написать:

   x:=0; while x<=2 do begin
     writeln(x,f(x));
     x:=x+0.2;
   end;

но вещественные вычисления на ЭВМ не точны,  потому  что,  к  примеру,
десятичное  0.2  в   машинной   двоичной  системе  счисления  является
иррациональным числом, и его округление, разумеется, приводит к потере
информации. Поэтому   нет  никакой  гарантии,  что  последняя  строчка
таблицы (при x=2) будет напечатана.
   Есть два   пути   решения   проблемы - использовать  цикл  с  целым
параметром:

   for i:=0 to 10 do begin
     x:=i/5.0;
     writeln(x,f(x));
   end;

либо изменить условие окончания цикла, допуская погрешность вычисления
x плюс-минус половина от шага цикла:

   x:=0; while x<=2.1 do begin
     writeln(x,f(x));
     x:=x+0.2;
   end;

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

   5. Вычисление  периодических  функций   (приведение   аргумента   в
заданный диапазон). Такая задача возникает достаточно часто (например,
аргумент синуса с точки зрения математики должен быть в  диапазоне  от
нуля до 2п,  в противном случае перед вычислением его следует привести
в указанный диапазон). Пусть диапазон ограничен величинами a и b:

   var a,b,x,d:real; n:integer;
   ...
   d:=b-a;           {величина диапазона}
   n:=trunc((x-a)/d);{сколько периодов отделяет x от начала диапазона}
   x:=x-d*n;         {приводим x в заданный диапазон}

          Использование промежуточных (буферных) переменных

   1. Пусть требуется поменять местами значение переменных a и b.
Если представить  переменные  в  виде  наполненных   кружек,   решение
приходит само  собой - нужно использовать дополнительную пустую кружку
(буфер для временного хранения данных):

 1)  │░│ x │▓│    2)  │ │   │▓│     3)  │▓│   │ │     4)  │▓│   │░│
    a└─┘│ │└─┘b       └─┘│░│└─┘         └─┘│░│└─┘         └─┘│ │└─┘
        └─┘              └─┘               └─┘               └─┘
                        x:=a;             a:=b;             b:=x;

   Классическим примером   на   эту   тему  является  транспонирование
матрицы:

   for i:=1 to n-1 do
     for j:=i+1 to n do begin
       x:=a[i,j];  a[i,j]:=a[j,i];  a[j,i]:=x;
     end;

   Кстати говоря,  с  транспонированием  матрицы  связана   еще   одна
распространенная ошибка  (for  i:=1  to  n  do  for j:=1 to n do ...).
Обратите внимание,  что необходимо просмотреть только  верхнюю  правую
(или нижнюю левую) треугольную часть матрицы,  не включая даже главную
диагональ.

   Иногда при  сложной   обработке   двумерного   массива   приходится
использовать  целую  буферную  строку,  а  в  совсем тяжелых случаях -
другой такой же массив.  Но чувство меры никогда  не  должно  покидать
программиста  (например,  задача  "поменять местами первую и последнюю
строку матрицы" требует всего одной буферной переменной,  а  вовсе  не
целой строки, как часто делают студенты).

   2. Расчеты по рекуррентным формулам.
Пусть даны рекуррентные формулы:

   x[n+1]=f(x[n],y[n])
   y[n+1]=g(x[n],y[n])

   Первой классической   ошибкой    здесь    является    неоправданное
использование массива.  Расчеты  по  таким  формулам носят,  очевидно,
циклический (итерационный)  характер,  и  использованные  в   формулах
индексы относятся к номеру итерации (очередного шага расчета), а вовсе
не являются порядковыми номерами элементов массива.
   Анализ на необходимость массива проводим так:
   - нужно запоминать все вычисленные значения x и y? - нет;
   - сколько  предыдущих  значений  нужно  помнить  на  очередном шаге
расчета? - только пару значений x и y от предыдущего шага.
   Резюме: массив не нужен.

   Вторая проблема не так очевидна.  Рассмотрим,  казалось бы,  вполне
правильное кодирование этих формул:

   x:=f(x,y); y:=g(x,y);

   Первым оператором мы  уже  вычислили  новое  значение  x:  [n+1]-е.
Теперь второй       оператор      фактически      будет      вычислять
y[n+1]:=g(x[n+1],y[n]), в то время как там должно быть старое значение
x[n]. Значит,  следует  отдельно  сохранять старые значения в буферных
переменных (о чем, кстати, и было сказано в выше проведенном анализе).
Сколько нужно буферных переменных?  Фактически, достаточно одной - для
сохранения x[n].  Однако для единообразия программы, для автоматизации
программирования  и  во  избежание  ошибок  рекомендую  сохранять  все
переменные на очередном шаге расчета.  Или, что то же самое, вычислять
новые  значения,  помещая  их  в  буферные  переменные  с  последующим
копированием их в рабочие переменные x и y:

   x1:=f(x,y); y1:=g(x,y);  x:=x1; y:=y1;

где временные  буферные  переменные x1 и y1 предназначены для хранения
соответственно x[n+1] и y[n+1].

   Другая разновидность рекуррентных формул вида:

   x[n+1]:=f(x[n],x[n-1],...,x[n-k])

также требует  k  буферных  переменных  для  хранения  используемых  в
расчете предыдущих  значений  и  их последовательной перезаписи друг в
друга:

   x:=f(x0,x1,x2,...,xk);
   xk:=x...   {имеется в виду x[n-k+1]}
   ...
   x2:=x1;
   x1:=x0;
   x0:=x;

   Если к  достаточно  велико  (например,  больше  5),  то  есть резон
подумать о создании буферного массива:

   var i:integer; buf:array[0..k] of ...
   ...
   x:=f(buf);
   for i:=k downto 1 do buf[i]:=buf[i-1];
   buf[0]:=x;


     Вставка и удаление элемента в массив, или техника указателей

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

   1. Дана строка текста. Удалить из нее все символы '*'.
   Разумеется, эта  задача  из  области элементарной обработки данных;
она требует минимальных ресурсов - вводить дополнительный  массив  нет
никакой необходимости.
   Представьте себе  аналог   массива   (текстовой   строки)   -   ряд
заполненных кружек.  "Вылейте"  из соответствующих кружек символы '*'.
Что делать дальше? Разумеется, переливать из соседних кружек в пустые,
чтобы в стройном ряду не оставлять промежутков.  Однако сразу не ясно,
как сформулировать  алгоритм  такого  перемещения  элементов  массива.
Когда перед Вами ряд кружек - все хорошо видно, и сразу ясно, из какой
кружки в какую переливать.  Но представьте, что Вы работаете в темноте
наощупь. Очевидно,  необходимо  иметь  какой-то  указатель,  чтобы  не
потерять место,  в  котором  в  данный  момент  находитесь.  Например,
держать одной   рукой   пустую   кружку,  а  другой  искать  ближайшую
заполненную. Точно так же следует поступить и при программировании.
   Нарисуем картинку  -  в  подобных задачах хорошая картинка способна
сразу сделать решение очевидным.

             куда   откуда   конец        Алгоритм выглядит так:
                                       - указатель "откуда" должен
            ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─       пройти по массиву от начала до
исходное    │a│b│*│c│*│*│d│e│ │...     конца;
состояние   └─┴─┴─┴─┴─┴─┴─┴─┴─┴─          - если проверяемый символ -
             │ │ ┌─┘     │ │           не '*', то копировать его из
  копирование│ │ │ ┌─────┘ │           "откуда" в "куда", после чего
             │ │ │ │ ┌─────┘           сдвинуть на шаг вперед
            ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─       указатель "куда".
результат   │a│b│c│d│e│ │ │ │ │...        - после завершения цикла
            └─┴─┴─┴─┴─┴─┴─┴─┴─┴─       указатель "конец" передвинуть
                                      на указатель "куда" (ибо
                куда                   символов уже стало меньше).

   Предположим, что длина строки (указатель "конец") известна и  равна
n; указатели "откуда" и "куда" обозначим переменными i и j :

   var i,j,n:integer; c:array[1..255] of char;
   ...
   j:=1;               {указатель "куда" на начало массива}
   for i:=1 to n do    {цикл просмотра массива}
     if c[i]<>'*' then begin
       c[j]:=c[i];     {копирование очередного символа}
       j:=j+1;         {указатель "куда" надо теперь передвинуть}
     end;
   n:=j-1;             {новая длина строки}

   Если надо   удалить  не  один  символ,  а  несколько,  то  алгоритм
практически не усложняется - всего лишь меняется условие  проверки,  и
если найдено   совпадение,   надо   изменить   указатель  "откуда"  на
количество удаляемых символов, чтобы пропустить их, не проверяя (лучше
тогда использовать не цикл "for i", а цикл "while i").

   Многие студенты  сравнительно легко находят точно такое же решение,
где копирование осуществляется из одного массива в  другой.  А  зачем?
Может быть,  трудно  осознать,  что копировать можно "откуда" угодно и
"куда" угодно,  и совершенно все равно,  тот же самый это  массив  или
другой. Надо только позаботиться, чтобы данные копировались на пустое,
свободное место,  чтобы не испортить другие данные.  В случае удаления
элементов для  этого  следует просматривать массив слева направо,  а в
случае вставки - справа налево.

   2. Дана строка текста. Утроить все символы '*'.
   Эта задача  сложнее,  поскольку  заранее неизвестно,  сколько будет
звездочек в  массиве  и,  соответственно,  на  сколько  символов   его
придется раздвигать.  Самым  простым  решением  будет  при  нахождении
очередной звездочки сдвинуть  правую  часть  массива  на  две  позиции
вправо, и на освободившиеся места записать дополнительные звездочки:

   i                 var i,j,n:integer; c:array[1..255] of char;
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─  ...
│a│*│b│*│c│ │ │ │ │   i:=1; while i<=n do begin    {цикл просмотра}
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─    if c[i]='*' then begin     {вставка}
 │ │ │ │ │                for j:=n downto i+1 do
 │ │ │ │ └───────┐          c[j+2]:=c[j];   {раздвижка массива}
 │ │ │ └───┐     │        n:=n+2;           {длина строки изменилась}
 │ │ └───┐ │     │        c[i+1]:='*';
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─      c[i+2]:='*';      {вставка нового текста}
│a│*│*│*│b│*│*│*│c│       i:=i+2;           {пропускаем новые символы}
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─    end;
                    i:=i+1;             {к следующему символу}
     * *     * *      end;

                        9. ПОДПРОГРАММЫ  (4 ч)

   Подпрограммы -  это  оформленные  в  виде  отдельных  блоков  части
программы, которые   можно   многократно  вызывать  на  выполнение  из
различных частей программы. Использованием подпрограмм достигается:
   - экономия памяти из-за уменьшения объема программы;
   - возможность ведения и использования библиотек готовых подпрограмм;
   - увеличение наглядности программы;
   - упрощение разработки больших программ в соответствии с принципами
процедурного программирования.
   Сложные программы  с  использованием  подпрограмм приобретают четко
выраженную иерархическую структуру:
                   ┌───────────┐             уровни вложенности:
             ┌─────┤ программа ├──────┐
             │     └─────┬─────┘      │          -  1
          ┌──┴──┐     ┌──┴──┐      ┌──┴──┐
        ┌─┤ п/п ├┐   ┌┤ п/п ├┐    ┌┤ п/п ├─┐     -  2
        : └─────┘:   :└─────┘:    :└─────┘ :     ...
     ┌──┴──┐┌────┴┐┌─┴───┐┌──┴──┐┌┴────┐┌──┴──┐
     │ п/п ││ п/п ││ п/п ││ п/п ││ п/п ││ п/п │  -  n
     └─────┘└─────┘└─────┘└─────┘└─────┘└─────┘

   Выделяют две основных тенденции при разработке больших программ:
   - снизу  вверх,  когда  сначала  создаются  мелкие  алгоритмические
кирпичики-подпрограммы, затем из них складываются блоки  покрупнее  и,
наконец, вся программа (такой способ разработки навязывает,  например,
язык FORTH);
   - сверху  вниз,  когда  сначала  определяется  в общих чертах,  что
делать, а затем задача разбивается на подзадачи и так конкретизируется
до самого низкого уровня.
   Первый подход более свойствен хаотическому программированию.  Может
оказаться так,  что  на более верхних уровнях программист будет скован
плохо продуманными базовыми кирпичиками,  отчего постоянно будет к ним
возвращаться и программа в результате будет вся в заплатах.
   Второй подход более  отвечает  идеям  структурного  и  процедурного
программирования. Он позволяет на начальных этапах абстрагироваться от
несущественных деталей и сосредоточиться на  решении  основных  задач.
Однако в  этом случае сильно затрудняется отладка программы.  Одним из
распространенных приемов здесь являются "заглушки" - пустые или сильно
упрощенные подпрограммы,  которые  ставятся на место еще не написанных
подпрограмм для отладки программы в целом.
   Практически при  разработке больших программ сочетаются оба метода.
Проектирование обычно включает следующие этапы:
   1) постановка задачи и выбор структуры данных;
   2) задача  разбивается  на  подзадачи,  каждая  из  которых   будет
реализована в  виде подпрограммы.  Этот процесс повторяется для каждой
подзадачи до  выделения  элементарных  подзадач.  Элементарными  можно
считать подзадачи:
   - реализация которых в виде алгоритмов и  программ  очевидны  и  не
вызывают затруднений;
   - которые могут быть решены использованием  имеющихся  библиотечных
подпрограмм (стандартных,  ранее  разработанных  -  своих  или  других
разработчиков);
   3) программирование  и  отладка  всех  подпрограмм  (обычно   снизу
вверх).
   Общие замечания:
   - качество проекта определяется в основном 1 и 2-м этапами; сегодня
наиболее качественными и надежными являются разработки  одного  автора
(или, по  крайней  мере,  руководителя  проекта),  так  как  при  этом
достигается наивысшее концептуальное единство всей программы;
   - классики  считают,  что  объем  одного  модуля  (подпрограммы) не
должен превышать 50 операторов,  иначе  резко  возрастает  вероятность
ошибок и затрудняется модификация программы.

                      Классификация подпрограмм

   По способу использования:
   - процедура  (procedure)   -   вызывается   отдельным   оператором,
например, "Read(x,y)";
   - функция  (function)  -  возвращает  в  качестве  результата  одно
вычисленное в   ней   значение,   что   позволяет  использовать  ее  в
выражениях; например, "r:=sin(x)+sqr(cos(y))".

   По назначению и способу доступа к данным:
   - для  выполнения ряда действий,  не связанных с обработкой данных;
соответственно, не требует параметров.  Например,  процедура  "readln"
без параметров просто переводит строку;
   - для выполнения ряда действий над конкретными  данными  вызывающей
программы (глобальными переменными). Не требует параметров;
   - для выполнения ряда действий по единому  шаблону  над  различными
переменными определенного   типа;   конкретные   данные  передаются  в
подпрограмму с  помощью  параметров.  Это  самый  важный  механизм   в
подпрограммах, который   дает  очень  широкие  возможности.  Например,
функция "sin" вычисляет синус от любого выражения,  переданного  ей  в
качестве параметра.  Отметим,  что в BASIC'е и некоторых других языках
среднего и низкого уровня механизм параметров отсутствует,  потому  на
них и   невозможно  написать  сколько-нибудь  серьезные  программы.  В
ассемблерах механизм параметров  также  не  предусмотрен,  программист
должен сам заботиться о передаче данных в подпрограмму.

   По расположению и способу описания:
   - стандартные - не требуют описания (например,  стандартные функции
типа sin,  abs и пр.);  расположены в стандартной библиотеке объектных
модулей PASCAL'я и присоединяются к программе в процессе компоновки;
   - внешние  (библиотечные)  -  описывается  только заголовок (способ
вызова и список параметров),  чтобы транслятор  мог  проконтролировать
правильность их вызова.  Эти подпрограммы могут быть написаны отдельно
от программы и даже на другом языке;  Вы можете их купить уже  в  виде
объектной библиотеки. Присоединяются к программе при компоновке;
   - внутренние (описанные внутри программы) - и  заголовок,  и  текст
самой подпрограммы находятся внутри текста программы.

                         Описание подпрограмм

   Заголовок подпрограмм описывается следующим образом:

   procedure <имя процедуры>[<список параметров>];
   function <имя функции>[<список параметров>]:<тип результата>;

   После заголовка следуют либо ключевые слова:

   external;  {подпрограмма внешняя (описана в другом модуле)}
   fortran;   {только  для  PASCAL/RT-11   -   указание,  что  внешняя
подпрограмма написана на ассемблере MACRO или на FORTRAN/RT-11}
   forward;   {тело подпрограммы описано ниже}

   либо описание тела подпрограммы,  которое,  как и описание основной
программы, может включать описание типов, переменных, меток, вложенных
подпрограмм и,  наконец,  операторы самой подпрограммы,  заключенные в
операторные скобки begin-end.
   Функция может  возвращать  в  вызывающую  программу значение только
простых типов (не массивов или  записей!).  Чтобы  функция  вернула  в
программу значение, надо присвоить его имени функции.
   В необязательном   списке   параметров   через   ";"  перечисляются
формальные параметры подпрограммы - их имена и тип.  Точно так же, как
в алгебре  под  X  подразумеваются  любые  возможные  значения,  а при
вычислении по формуле вместо X мы подставляем конкретные числа,  так и
при вызове  подпрограммы  указываются  конкретные  данные (фактические
параметры), с которыми подпрограмма и будет выполнять все действия.

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

   Правила использования параметров:
   - количество,  порядок  следования  и  тип фактических и формальных
параметров должны совпадать;
   - входные  данные  в  подпрограмму  следует передавать по значению,
тогда никакие их изменения в подпрограмме не  повлияют  на  вызывающую
программу;
   - выходные данные (которые вычисляются или изменяются в  процедуре)
следует передавать по ссылке;
   - данные больших размеров (например, массивы) следует передавать по
ссылке, чтобы   программа   не   тратила   память  на  соответствующие
формальные параметры  и  время  на  их  копирование (только необходимо
заботиться о том, чтобы не внести ненужных изменений в эти данные);
   - из-за   ограничений   трансляторов    PASCAL'я    при    описании
параметров-массивов и   других   сложных  типов  следует  пользоваться
средствами описания новых типов:

   type Matrix=array[1..24,1,32] of real;
   var M:Matrix;
   procedure Work(X:Matrix);
   ...
   Work(M);

   ПРИМЕР 1.  Уменьшение  размеров   программы   за   счет   вынесения
повторяющихся действий в подпрограмму.
   Пусть мы хотим напечатать некий абстрактный текст,  отдельные части
которого отделены строками звездочек:

                         begin
   *************           writeln('*************');
   начало                  writeln('начало');
   *************           writeln('*************');
   середина                writeln('середина');
   *************           writeln('*************');
   конец                   writeln('конец');
                         end.

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

     procedure Line;              {описание процедуры}
     begin
       writeln('*************');
     end;
   begin                          {начало основной программы}
     Line; writeln('начало');
     Line; writeln('середина');
     Line; writeln('конец');
   end.

   ПРИМЕР 2. То же самое, но более универсальное за счет использования
параметров. Давайте научим нашу процедуру рисовать линию любой длины и
из любых символов:

   var i:integer;                          {переменные программы}
     procedure Line(sym:char; n:integer);  {описание процедуры}
     var i:integer;                        {переменные процедуры}
     begin                                 {тело процедуры}
       for i:=1 to n do write(sym);
       writeln;
     end;
   begin                                   {начало программы}
     Line('*',10);
     for i:=0 to 4 do Line(chr(ord('a')+i), i+1);
     Line('*',10);
   end.                               **********
                                      a
   Результатом будет:                 bb
                                      ccc
                                      dddd
                                      eeeee
                                      **********
   Итак, особенности и правила:
   - подпрограмма,  как и любой объект в PASCAL'е, должна быть описана
до того, как использована;
   - подпрограммы могут быть вложены друг в друга, как матрешки (число
вложений теоретически не ограничено);
   - с помощью оператора перехода GOTO нельзя войти в подпрограмму или
выйти из нее;
   - локальные  объекты  (переменные и подпрограммы,  описанные внутри
данной подпрограммы)   неизвестны   и    недоступны    снаружи    этой
подпрограммы;
   - в подпрограмме n-го уровня вложенности известны  и  доступны  все
объекты, описанные  на уровнях с 1 до n-1 (для данной подпрограммы эти
объекты будут называться глобальными, или внешними);
   - если   имена  локальных  переменных  будут  совпадать  с  именами
глобальных переменных  (как  в  примере  2),   ничего   страшного   не
произойдет -  PASCAL  в  этом  случае  будет  подразумевать  локальные
переменные (то есть переменная i подпрограммы  Line  в  примере  2  не
будет иметь ничего общего с переменной i основной программы);
   - если же  в  примере  2  вдруг  убрать  описание  переменной  i  в
подпрограмме Line,   получится   весьма   распространенная   и  трудно
обнаруживаемая ошибка: подпрограмма будет менять переменную i, которая
в программе  используется  как  параметр  цикла.  Классики очень точно
обозвали подобные ошибки "эффектом спагетти" - дернешь в одном  конце,
вылезет в другом.
   Приведем для примера схему некоторой  программы  и  прокомментируем
вложенность подпрограмм  и  вытекающую  из  нее доступность процедур и
переменных:

┌───────────────────┐   из модуля│  можно  │    нелзья    │доступны
│ program (0)       │            │ вызвать │    вызвать   │переменные
│ ┌───────────────┐ │   ─────────┼─────────┼──────────────┼───────────
│ │procedure 1    │ │       0    │ 1, 4    │ 2, 3, 5, 6   │ 0
│ │ ┌───────────┐ │ │            │         │ (локальные)  │
│ │ │procedure 2│ │ │            │         │              │
│ │ └───────────┘ │ │       1    │ 1, 2, 3 │ 4 (не описан)│ 0, 1
│ │ ┌───────────┐ │ │            │         │ 5,6(локальны)│
│ │ │procedure 3│ │ │            │         │              │
│ │ └───────────┘ │ │       2    │ 1, 2    │ 3 (не описан)│ 0, 1, 2
│ └───────────────┘ │            │         │ 4, 5, 6      │
│ ┌───────────────┐ │            │         │              │
│ │procedure 4    │ │       3    │ 1, 2, 3 │ 4, 5, 6      │ 0, 1, 3
│ │ ┌───────────┐ │ │            │         │              │
│ │ │procedure 5│ │ │       4    │ 1,4,5,6 │ 2, 3         │ 0, 4
│ │ └───────────┘ │ │            │         │              │
│ │ ┌───────────┐ │ │       5    │ 1, 4, 5 │ 2, 3, 6      │ 0, 4, 5
│ │ │procedure 6│ │ │            │         │              │
│ │ └───────────┘ │ │       6    │ 1,4,5,6 │ 2, 3         │ 0, 4, 6
│ └───────────────┘ │   ─────────┴─────────┴──────────────┴───────────
└───────────────────┘

   Обратите внимание,  что   из   подпрограммы   можно   вызвать   как
подпрограммы одинакового  и более высоких уровней,  ранее описанные во
внешних подпрограммах,  так и саму себя - поскольку  свой  собственный
заголовок уже   известен.   Прямой   или   косвенный   (через   другие
подпрограммы) вызов какой-либо  подпрограммой  самой  себя  называется
рекурсией. Эту  "высшую  математику"  программирования  мы  рассмотрим
позднее; пока  же  упомянем  только иногда возникающую в таких случаях
проблему. Например, из процедуры 1 потребуется вызывать процедуру 4, а
она еще не описана. Можно было бы поменять местами описание процедур 1
и 4,  но из 4 вызывается 1 - получается замкнутый круг.  Его разрывает
ключевое слово "forward":

   program ...
   procedure 4(...); forward;  {описывается только заголовок}
   procedure 1(...);
   begin
     ...  {здесь вызывается процедура 4}
   end;
   procedure 4(...);  {процедура описывается полностью}
   begin
     ...
   end;

   Теперь поговорим  о  подпрограммах,   которые   предназначены   для
каких-либо операций   с  данными.  Напишем,  например,  процедуру  для
удвоения переменной:
   var x:integer;
     procedure D;
     begin
       x:=x*2;   {удваиваем глобальную переменную x}
     end;
   begin
     x:=3; D; writeln(x); {6}
     D; writeln(x);       {12}
   end.

   Очевидно, что эта подпрограмма не  универсальна.  Если  мы  захотим
использовать ее  для удвоения какой-либо другой переменной,  например,
y, то будем вынуждены написать:  x:=y; D; y:=x;
   К сожалению, только такой способ работы с подпрограммами и возможен
в языке BASIC, за что его и ругают все нормальные люди.
   Напишем более универсальную процедуру,  которая  позволит  изменять
любые переменные. Разумеется, используем механизм параметров:

   var x,y:integer;
     procedure D(var x:integer); {x - входной и выходной параметр}
     begin
       x:=x*2;
     end;
   begin
     x:=2; y:=3;
     D(x); D(y);
     writeln(x,y);   {4,6}
   end.

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

   var x:integer;
     function D(x:integer):integer;
     begin
       D:=x*2;  {возвращаемое значение}
     end;
   begin
     x:=D(3);           {6}
     writeln(2*D(x-1)); {20}
   end.

                    10. ВВОД И ВЫВОД ДАННЫХ (2 ч)

   Мы уже  говорили,  что  в самом общем смысле любая программа на ЭВМ
занимается переработкой информации.  Следовательно,  ЭВМ с  работающей
программой можно  рассматривать  как черный ящик,  в который поступает
некоторая информация   (входные   данные)   и   выдается   другая    -
переработанная или новая информация (выходные данные).
   Источником входных   данных   являются   такие   устройства,    как
клавиатура, мышь, диск, датчики (для управляющей ЭВМ). Выходные данные
могут выдаваться  на  дисплей,  принтер,  диск,  генератор  звука,  на
управление технологическим    оборудованием    и    т.п.   Разумеется,
программист, работающий на языке высокого уровня, обычно не опускается
до конкретного   управления  устройствами.  Он  использует  для  этого
многоуровневую цепочку,    организуемого     системным     программным
обеспечением: оператор    ввода/вывода   языка   высокого   уровня   
стандартный запрос на ввод/вывод к операционной системе    управление
конкретным устройством  с  помощью  драйвера.
   Наиболее часто встречаются следующие адресаты ввода/вывода:
   - клавиатура и дисплей (стандартный вывод);
   - магнитный диск (программа занимается преобразованием  информации,
хранящейся на магнитном диске).
   На диске информация для удобства работы с ней организуется  в  виде
отдельных блоков  -  файлов  (подробнее  о  них  поговорим  в  разделе
"Операционные системы").  Если для операционной  системы  файл  -  это
минимальная единица  информации,  то  наша программа может работать со
внутренностями файла.  Образно говоря,  файл похож на  книгу,  которая
стоит на  полке  (диске).  С  помощью  операционной  системы  мы можем
перенести ее с полки на полку,  выбросить  и  т.п.  А  наша  программа
работает с книгой в следующем порядке:
   - открывает книгу для чтения - reset (или пустой блокнот для записи
- rewrite);
   - читает ее - read (или пишет - write);
   - закрывает - close (ставит обратно на полку).

   К сожалению,  полная машинная независимость программы на языке даже
высокого уровня не достигается,  поскольку программисту все равно надо
учитывать особенности файловой системы;  поэтому реализации  одного  и
того  же  языка  (например,  PASCAL)  в  разных  операционных системах
отличаются прежде всего операторами работы с файлами.
   Работа с файлами в стандартном PASCAL'е имеет особенности:
   - поддерживается   только   работа   с   текстовыми   файлами  (что
неэффективно, когда надо хранить большие объемы числовых данных);
   - реализован  только  последовательный  механизм  работы  с файлами
(книгу можно листать только вперед, но не назад);
   - файл  можно только читать,  если он открыт на чтение,  или только
записывать, если он открыт на запись.  Прочитать уже записанное  можно
только одним образом - закрыть записанный файл и снова открыть его уже
для чтения.
   Turbo Pascal имеет дополнительные операторы для удобства  работы  с
файлами и  для  организации  прямого  доступа к любой записи файла,  а
также файлы не только текстовые, но и из любых типов данных.
   Во всех операторах ввода/вывода указывается  адресат  -  логическое
имя открытого    файла.    Однако    для   стандартного   ввода/вывода
(клавиатура/дисплей) этого делать не нужно - на то он  и  стандартный.
Стандартные файлы ввода  INPUT  и  вывода  OUTPUT  всегда  открыты,  и
закрывать их также не требуется.
   Единственным знакомым    мне    транслятором,    который    требует
использования  стандартного  заголовка  программы   "program   Example
(input,output);"  является PASCAL на ЕС-1022.
   Операторы ввода:  read(<список  ввода>)  и  readln(<список ввода>);
вывода:  write(<список вывода>) и writeln(<список вывода>).  Окончание
"ln" означает  требование  перевода строки после выполнения оператора.
Список ввода -  это  перечисленные  через  запятую  имена  переменных,
значения которых  оператор  должен  ввести  с клавиатуры (или читаемых
программой из файла на диске). Список вывода - это тексты, переменные,
выражения, значения  которых  будут  выведены  на  дисплей  (в файл на
диске). При наборе вводимых чисел на  клавиатуре  их  можно  разделять
пробелами, запятыми или возвратом каретки (Enter на  IBM  PC).  Однако
надо помнить о разных ситуациях, которые могут иметь место при вводе:

   program aaa(input,output);       Допустимый ввод данных:
   var i,j,k:integer;
   begin                            1        или      1
     readln(i);                     2 3               2
     readln(j,k);                                     3
   end.

   Если же ввести числа в одну строку:  1 2 3,  то программа прочитает
первое число,  затем  "переведет строку",  и последние числа пропадут.
Программа вновь будет ждать ввода оставшихся двух чисел.
   Вещественные числа  при  вводе  можно  записывать  как  целые  (без
точки), а вот если при вводе целого  числа  написать  вещественное  (с
точкой), возникнет  ошибка  ввода  и  программа  аварийно  завершится.
Вообще при вводе чисел недопустимо использовать какие-либо посторонние
символы; нельзя также при вводе пользоваться стрелками для исправления
вводимой строки, если операционная система этого не поддерживает.

   Как мы  уже  говорили,  при  написании  программы  всегда   следует
заботиться о  диалоге с оператором.  Даже в простейших программах надо
предусматривать хотя бы простейшую подсказку о том,  что она делает  и
что конкретно   требуется   от   оператора.  Ибо  никто,  кроме  самой
программы, не подскажет ему этого лучше.
   В стандартном  PASCAL'е  нет  средств  управления  экраном (в Turbo
Pascal есть),  поэтому и ввод и вывод происходят с того места,  где  в
данный момент  времени  стоит  курсор (наследие от первых терминалов в
виде электрических пишущих машинок). Это ограничивает сценарий диалога
программы с оператором простейшей схемой "вопрос-ответ":

   program bbb(input,output);
   var s,v,t:real;
   begin
     writeln('программа расчета времени пути');
     write('введите путь     : '); readln(s);
     write('введите скорость : '); readln(v);
     writeln('время = ',s/v);
   end.

   Такая программа даже для незнакомого с  ней  оператора  не  требует
дополнительных комментариев:

   программа расчета времени пути
   введите путь     : 200
   введите скорость : 5
   время = 4.000000e01

   Обратите внимание,  что подсказка для ввода  печатается  оператором
write, а не writeln, чтобы курсор оставался в той же строке.
   PASCAL позволяет также  управлять  форматом  вывода  чисел.  Формат
может задаваться  отдельно  для  каждого элемента списка вывода (здесь
x - количество позиций для размещения числа, y - количество цифр после
десятичной точки для вещественных чисел):
──────────────────────────┬──┬──┬─────────────────────────────────────
   программа              │ x│ y│   результат
──────────────────────────┼──┼──┼─────────────────────────────────────
program ccc(input,output);│ 5│ 2│ r= 3.14 i=  -45
var r:real; i:integer;    │ 6│ 3│ r= 3.142 i=   -45
begin                     │ 8│ 4│ r=  3.1416 i=     -45
  r:=3.1415926; i:=-45;   │13│ 0│ r= 3.141593e+01 i=          -45
  write('r=',r:x:y,       │   при отсутствии второго или обоих
       ' i=',i:x);        │   указателей формата вещественное число
end.                      │   выводится в показательной форме
──────────────────────────┴───────────────────────────────────────────

   При работе  с  нестандартными   файлами   надо   уже   использовать
специальную файловую переменную, например:
   var f:text;  {определен стандартный тип type text=file of char}
──────────┬───────────────────────────────────────────────────────────
          │              примеры  для  PASCAL/RT-11
действия  ├───────────────────────────┬───────────────────────────────
          │  чтение файла             │  запись файла
──────────┼───────────────────────────┼───────────────────────────────
открыть   │ reset(f,'имяфайла');      │ rewrite(f,'имяфайла');
работать  │ readln(f,<список ввода>); │ writeln(f,<список вывода>);
закрыть   │ close(f);                 │ close(f);
──────────┴───────────────────────────┴───────────────────────────────
   В Turbo  Pascal'е  введена  специальная  процедура Assign(<файловая
переменная>,<имя файла>),  а процедуры reset(<файловая переменная>)  и
rewrite(<файловая переменная>) соответствуют стандарту.
   При работе с  файлами  используются  также  стандартные  логические
функции eof(<файловая   переменная>)  и  eoln(<файловая  переменная>),
которые возвращают значение true  при  возникновении  ситуаций  "конец
файла" или "конец строки".

   ПРИМЕР: Распечатка на дисплее текстового файла.

   var f:text; c:char;
   begin
     reset(f,'a.pas');      {будем работать с файлом a.pas}
     while not eof(f) do      {цикл до конца файла}
       if eoln(f) then begin    {если конец строки}
         writeln;                 {переводим строку на дисплее}
         readln(f);               {переводим строку входного файла}
       end else begin           {иначе}
         read(f,c);               {читаем очередной символ}
         write(c);                {выводим его на дисплей}
       end;
     close(f);              {закрываем файл}
   end.


     11. СОСТАВНЫЕ СТРУКТУРЫ ДАННЫХ (ЗАПИСИ) В ЯЗЫКЕ PASCAL (1 ч)

   Запись -  это сложная структура данных,  составленная из некоторого
количества элементов  (полей),  которые  могут  иметь  различный  тип.
Собственно, запись  придумана  только  для  того,  чтобы  объединять в
единое целое информацию о каком-либо описываемом объекте.
   Используя записи, мы можем, например, ввести в свою программу новый
тип данных - комплексные числа:

   type Complex=record
     r,i: real; {запись имеет два поля данных для вещественных}
   end;                              {и мнимых компонент числа}

   {сложение комплексных чисел}
   procedure CAdd(a,b:Complex; var Res:Complex);
   begin
     Res.r:=a.r+b.r;  {используем соответствующие поля данных}
     Res.i:=a.i+b.i;
   end;

   {умножение комплексных чисел}
   procedure CMul(a,b:Complex; var Res:Complex);
   begin
     Res.r:=a.r*b.r+a.i*b.i;
     Res.i:=a.r*b.i+b.r*a.i;
   end;

   {печать комплексного числа с указанием формата}
   procedure CWrite(a:Complex; F1,F2:integer);
   begin
     with a do write('(', r:F1:F2, ',', i:F1:F2, 'i);
   end;

   Итак:
   - доступ  к  полю  записи осуществляется указанием имени поля после
имени переменной типа "запись" (имена разделяются точкой);
   - оператор  "with  <имя  переменной> do ..." позволяет не указывать
имя переменной при обращении к ее полям.
   В языке PASCAL допускаются также записи с вариантами:

   type
     Полноеимя=record
       имя, фамилия: array[1..10] of char;
     end;
     Издание=record
       Автор: Полноеимя;
       Название,издательство: array[1..44] of char;
       case вид:(книга,метод_пособие,журнал,газета) of
         книга,метод_пособие: (год_к: integer);
         журнал: (
           номер: 1..12;
           год_ж: integer);
         газета: (
           день: 1..31;
           месяц: 1..12;
           год_г: integer);
     end;
     ...
   var E:издание;
     with E do begin
       Автор.имя='Алексей   '; Автор.фамилия:='Донской   ';
       Название:=    'Система имитационного моделирования АЛМИК   ';
       Издательство:='Издательство Чувашского университета        ';
       вид:=метод_пособие;
       год_к:=1991;
     end;

   Обратите внимание на следующие ограничения:
   - вариантная часть может располагаться только в конце записи;
   - одинаковые поля разных частей  должны  называться  по-разному  (в
данном примере  можно  было  вынести одинаковое для всех поле "год" из
вариантной части,  но не всегда это возможно).
   Допускаются записи  с  вариантами  без  конкретного  поля-указателя
варианта, например,  запись для передачи событий в среде Turbo  Vision
для Turbo Pascal:

TEvent = record
  What: Word;                    {класс события}
  case Word of
    evNothing: ();               {пустое событие}
    evMouse: (                     {событие от мыши}
      Buttons: Byte;               {состояние кнопок}
      Double: Boolean;             {двойное нажатие}
      Where: TPoint);              {координаты}
    evKeyDown: (                 {событие от клавиатуры}
      case Integer of
        0: (KeyCode: Word);
        1: (CharCode: Char; ScanCode: Byte));
    evMessage: (                 {сообщение}
      Command: Word;                {код команды}
      case Word of
        0: (InfoPtr: Pointer);      {вспомогательная информация}
        1: (InfoLong: Longint);
        2: (InfoWord: Word);
        3: (InfoInt: Integer);
        4: (InfoByte: Byte);
        5: (InfoChar: Char));
end;

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


               12. ДИНАМИЧЕСКИЕ СТРУКТУРЫ ДАННЫХ (3 ч)

   Мы уже говорили,  что существенным недостатком статических структур
данных (например,   массивов)  является  их  предопределенный  размер,
который не может изменяться  при  выполнении  программы.  Но  в  любой
более-менее сложной  программе  требуется  обрабатывать данные,  объем
которых заранее неизвестен.  Для их размещения в  памяти  используются
динамические переменные.    Динамические   переменные   также   удобно
применять для организации сложных структур данных (см.ниже).
   Обычно вся оставшаяся свободной  после  загрузки  программы  память
отводится исполняющей  системой  PASCAL под стек и динамическую память
(heap). Стек используется для сохранения параметров и адреса  возврата
при вызове  подпрограмм,  а также для размещения локальных переменных.
Динамическая память используется для временного хранения  динамических
переменных. Порядок работы с динамической переменной:
   - создать (отвести для нее место в динамической памяти;
   - работать с ней при помощи указателя (адреса ее начала);
   - удалить (освободить занятое этой переменной место.
   Уже из этого перечисления ясны достоинства динамических переменных:
   - данные, размещаемые в них, могут занимать все свободное ОЗУ;
   - после     удаления     отработавших    динамических    переменных
освободившуюся динамическую память можно снова использовать.
   Приведенный ниже  пример покажет работу с динамическими переменными
и указателями:

   var p1,p2:^real;  {описание указателей на  динамическую  переменную
     типа real (конкретный тип нужен для определения размера)}
   ...
   New(p1); {создать  динамическую переменную и присвоить указателю p1
            значение ее адреса}
   p1^:=3.1415;  {заполнение динамической переменной}
   p2:=p1;       {теперь указатель p2 показывает на ту же переменную}
   writeln(p2^); {можно в этом убедиться, распечатав ее значение}
   dispose(p1);  {удалить переменную, освободив динамическую память}

   Работать с  динамическими  переменными  надо  очень аккуратно,  ибо
здесь любая алгоритмическая ошибка приведет к фатальным  последствиям,
а отлаживать  такие  программы  очень трудно даже на Borland Pascal'е.
Например, после удаления  динамической  переменной  в  вышеприведенном
участке программы   адреса,  хранящиеся  в  указателях  p1  и  p2,  не
изменяются. Если программа попытается  вновь  обратиться  к  удаленной
переменной через  эти  указатели,  ничего страшного не произойдет.  Но
следующая вновь создаваемая динамическая переменная  будет,  вероятно,
размещена в  этом  же участке памяти,  и рано или поздно данные начнут
меняться как бы сами собой - найти такую ошибку очень трудно. Еще хуже
может получиться ситуация, когда при записи в уже удаленную переменную
нарушается структура  списка  свободной  динамической  памяти.   Тогда
программа в  лучшем  случае  зациклится,  а  в  худшем - испортит саму
себя или даже операционную систему  (на  СМ-4  последнее,  к  счастью,
невозможно). Можно сформулировать некоторые правила:
   - после удаления динамической переменной обнуляйте  соответствующие
указатели (для нулевого адреса в PASCAL'е есть константа nil);
   - перед  использованием  указателя  проверяйте   допустимость   его
значения (хотя бы сравнив его со значением nil);
   - рисуя   предварительно   динамическую   структуру   на    бумаге,
изображайте стрелками  свои  указатели  и  операции  над  ними  - этот
простой до банальности прием позволяет  избежать  большого  количества
ошибок, поскольку держать в голове всю структуру невозможно.
   Особенности Turbo Pascal:
   - слава  богу,  существуют  нетипизированные  указатели  (pointer),
позволяющие обойти  слишком  строгий  синтаксис   PASCAL'я,   делающий
невозможным реализацию ряда сложных программ;
   - при  работе  с   указателями   (адресами)   широко   используется
приведение типов  (для  приведенного  примера  можно описать тип "type
pint=^integer" и работать с созданной динамической  переменной  как  с
целой: "pint(p1)^:=pint(p1)^ mod 2;". Иногда и это бывает нужно);
   - существуют процедуры захвата и освобождения участков динамической
памяти произвольного  размера,  а  не  обязательно определенного типом
указателя: GetMem(var  p:pointer;  Size:word)   и   FreeMem(p:pointer;
Size:word).

                          Односвязный список

   Простейшей динамической   структурой  данных  является  односвязный
список:             ┌─────────┐     ┌─────────┐            ┌─────────┐
┌─────────┐  ┌─────│ данные  │  ┌─│ данные  │  ┌      ──│ данные  │
│указатель├──┘      ├─────────┤  │  ├─────────┤  │  ...    ├─────────┤
│на начало│         │указатель├──┘  │указатель├──┘         │  nil    │
└─────────┘         └─────────┘     └─────────┘            └─────────┘
   Односвязный список  можно  рассматривать как аналог массива,  но со
следующими особенностями:
   - спиок  удобно  составлять  из  переменных типа запись,  поскольку
нужен и доступ к данным элемента, и работа с ним как с единым целым;
   - список может иметь произвольный размер;
   - доступ к  элементу  списка  уже  не  прямой,  как  в  массиве,  а
последовательный (надо   поставить   указатель  в  начало  списка,  и,
двигаясь по цепочке,  отсчитать  нужное  количество  элементов),  что,
разумеется, замедляет работу с ним;
   - вставка элемента в список,  напротив,  осуществляется быстрее (не
надо перемещать  в памяти оставшийся "хвост" массива;  достаточно лишь
отправить указатель очередного элемента на новый, а его указатель - на
следующий элемент  списка),  также  как  и  удаление (переместить один
указатель в списке - и все);
   - в  отличии  от  массива  список  может  состоять  из  разнотипных
элементов (лишь  бы  у каждого из них было поле указателя на следующий
элемент и какое-нибудь поле,  задающее размер элемента -  чтобы  можно
было единообразно удалять элементы списка).
   Приведем пример  ввода  текстовой  строки  произвольной  длины и ее
запоминания в виде односвязного списка:

type
  PEl=^TEl;        {указатель на элемент списка}
  TEl=record       {элемент списка}
    S: char;         {данные}
    Next: PEl;       {указатель на следующий элемент списка}
  end;
var
  c: char;
  Beg, p, p1: PEl;  {Beg - начало списка, p,p1 - рабочие указатели}
begin
  write('введите строку текста, кончающуюся точкой: ');
  Beg:=nil; p:=nil;         {обнулить указатели}
  repeat
    read(c);
    if c<>'.' then begin
      if Beg=nil then begin {самое начало списка}
        New(Beg);
        p:=Beg;
      end else begin        {очередные элементы}
        p1:=p;
        New(p);
        p1^.Next:=p;        {связать новый элемент со списком}
      end;
      p^.S:=c;              {заполнить поле данных}
      p^.Next:=nil;         {признак конца списка}
    end;
  until c='.';
  write('контроль введенной строки: ');
  p:=Beg;
  while p<>nil do begin
    write(p^.S);
    p:=p^.Next;
  end;
  writeln;
  p:=Beg;         {удаление элементов списка}
  while p<>nil do begin
    p1:=p^.Next;  {такой способ сохранения указателя обязателен,}
    Dispose(p);   {иначе после выполнения Dispose(p)}
    p:=p1;        {продолжение списка будет потеряно}
  end;
  Beg:=nil;       {обнулить указатель на начало списка}
end.

                          Двусвязный список

   Часто требующийся доступ к элементу списка по индексу требует,  как
уже было сказано, последовательного прохождения всего списка от начала
до искомого  элемента.  Если  Вы  вознамеритесь  обрабатывать список в
обратном порядке, затраты времени могут оказаться достаточно большими.
В таком  случае  применяют  двусвязный список (каждый элемент которого
имеет два указателя - на следующий и  на  предыдущий.  Соответственно,
несколько усложняется вставка и удаление элементов.
                    ┌─────────┐     ┌─────────┐            ┌─────────┐
┌─────────┐  ┌─────│ данные  │┐ ┌│ данные  │┐ ┌     ──│ данные  │
│указатель├──┘      ├─────────┤ │ │ ├─────────┤ │ │        ├─────────┤
│на начало│         │указатель├─│─┘ │указатель├─│─┘  ...   │  nil    │
└─────────┘         ├─────────┤ │   ├─────────┤ │          ├─────────┤
                    │   nil   │ └───┤указатель│ └──     ───┤указатель│
                    └─────────┘     └─────────┘            └─────────┘

                           Кольцевой список

   Если указатель последнего элемента односвязного списка направить на
начало списка, получится кольцевой список (для двусвязного списка надо
также указатель первого элемента направить  на  последний).  Кольцевой
список широко  применяется  в  различных алгоритмах (например,  список
элементов в группе (окне) Turbo Vision).

                         Многосвязный список

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

                       Структура типа "дерево"

   Дерево есть  частный случай направленного графа.  Дерево имеет одно
начало (корень) и много концов (листьев),  к которым приводят в  конце
концов  все  пути  по  его веткам.  Деревья очень широко применяются в
программах.  Например,  в виде дерева представляются возможные ходы из
текущей  позиции  при игре в шахматы или шашки.  Поскольку для полного
анализа всех возможных ходов не хватит никакой мыслимой памяти  ЭВМ  и
ее быстродействия, строящееся после каждого хода дерево ограничивается
некоторой определенной глубиной (количеством узлов графа  по  пути  от
корня  до листьев).  Затем с помощью рекурсивной минимаксной процедуры
выбирается ветка (ход),  ведущая в  узел  с  наибольшей  положительной
оценкой.
   В виде дерева представляются сценарии диалоговых программ; хотя это
и не из области структур данных,  а,  скорее, алгоритмизации задачи. А
вот структура данных типа "меню" (если  оно  имеет  подпункты)  -  это
типичное  дерево.  Другая  область  применения  дерева - аналитическое
представление формул. Например, выражение a*b+sin(c) можно представить
в виде дерева:
                ┌───┐                 где a, b и c являются листьями,
          ┌─────┤ + ├─────┐            а другие квадратики - узлами,
        ┌─┴─┐   └───┘   ┌─┴─┐           определяющими операцию над
  ┌─────┤ * ├─────┐     │sin├─────┐      ветками-операндами.
╔═╧═╗   └───┘   ╔═╧═╗   └───┘   ╔═╧═╗     Запись, описывающая узел
║ a ║           ║ b ║           ║ c ║      или лист такого дерева,
╚═══╝           ╚═══╝           ╚═══╝      может выглядеть так:
   type
     PNode=^TNode;
     TNode=record
       case Operation:(opConst,opMul,opAdd,opSub,opDiv,opSin,opCos) of
         opConst: (Value: real);
         opMul,opAdd,opMul,opDiv: (arg1,arg2: PNode);
         opSin,opCos: (arg: PNode);
     end;

                      Сложные многосвязные графы

   Сложные многосвязные графы применяются, например, для представления
структурной схемы  в системах имитационного моделирования (МОДС,  МИК,
АЛМИК). От дерева такие структуры отличаются тем,  что  они  не  имеют
начала (или  имеют  несколько  входных  точек) и конца,  а также могут
иметь несколько путей из одного узла в другой:
            ┌───┐
    ┌──────│ b ├───────────┐          Разумеется, все вышеописанные
    │       └───┘ ┌─────┐   │       структуры данных можно реализовать
 X  │  ┌───┐      │  1  │      Y   и с помощью статических массивов,
 ───┴─│ k ├─o──│ ─── ├──o─┬──  но эта очень трудоемкая работа
       └───┘     │ T p │     │     все равно приведет в конце концов
            ┌─┴─┐ └─────┘     │     к ручному программированию той же
            │ a │────────────┘     системы распределения памяти.
            └───┘

                           Стеки и очереди

   Стек и  его  разновидность - очередь - используется:
   - для организации очередей каких-либо событий;
   - для вызова подпрограмм и передачи параметров в подпрограммы;
   - для организации рекурсивных алгоритмов.
   Различают два вида стека:
   - LIFO (Last In,  First Out - последним зашел, первым вышел). Самая
лучшая иллюстрация   такого  стека  -  магазин  автомата  Калашникова.
Патроны по  очереди  заталкиваются  в  стек  (магазин),  и   последний
(верхний) патрон при стрельбе выйдет первым;
   - FIFO (First In,  First Out - первым  зашел,  первым  вышел).  Это
типичная очередь, которая имеет начало и конец (Head - голова и Tail -
хвост). Используется  обычно  для  организации   очереди   событий   в
операционных системах  и в программах.  Событием,  например,  является
нажатие на клавишу.  Можно  нажать  несколько  клавиш  подряд,  и  они
запомнятся в очереди. Затем, по мере готовности, программа будет брать
их из очереди и обрабатывать.
   По способу  реализации  различают статические и динамические стеки.
Статический стек представляет собой обыкновенный массив с  одним  (для
LIFO) указателем на вершину и двумя (для FIFO) указателями на голову и
хвост. Последний случай - очередь в массиве называют  также  кольцевым
буфером (голова все время гонится за хвостом). Достоинства статических
стеков - простота и быстрота работы с ними. Недостаток - фиксированный
размер, что  может привести к переполнению очереди и пропуску событий,
а при вызове подпрограмм - к аварийному завершению программы.
   Динамические стеки   обычно   программируются  в  виде  односвязных
списков в  динамической  памяти.  Достоинства  -  размер  заранее   не
определяется и  может занимать всю свободную память ЭВМ.  Недостаток -
сложность и низкая скорость их обработки.
   Рассмотрим подробнее   системный   стек,   который  используют  все
процессоры ЭВМ и программные средства для  вызова  подпрограмм.  Любой
процессор обычно имеет специальный регистр указателя стека (SP - Stack
Pointer), который показывает на вершину  стека  (PASCAL  СМ-4  и  ДВК,
например, отводит   для   стека  всю  свободную  память).  При  вызове
подпрограммы в стек  записывается  адрес  возврата  (для  возврата  из
подпрограммы), затем  параметры  этой  подпрограммы. Посмотрим пример:

   procedure Sum(a,b:integer; var c:integer);
   begin
     c:=a+b;
   end;
   ...
   Sum(x-y,z,R);

1) стек до вызова процедуры        2) стек при работе процедуры
      ├───────┤                        │ xxx                        │
sp ── │ xxx   │                        ├────────────────────────────┤
      ├───────┤                        │адрес возврата              │
      │ пусто │                        ├────────────────────────────┤
                                  a ── │значение выражения x-y      │
3) после выхода из процедуры           ├────────────────────────────┤
      ├──────────────────┤        b ── │значение выражения  z       │
sp ── │ xxx              │             ├────────────────────────────┤
      ├──────────────────┤        c ── │адрес выходной переменной R │
      │адрес возврата,   │             ├────────────────────────────┤
      │значение выражения│       sp ── │ пусто                      │
      │и т.д.            │

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


                         13. РЕКУРСИЯ  (2 ч)

   Рекурсия - это прием  программирования,  при  котором  подпрограмма
прямо или  косвенно  (через  другие  подпрограммы)  обращается к самой
себе. Классики   заметили,   что   рекурсия   трудна   для    взрослых
программистов, привыкших к BASIC'у или FORTRAN'у, где она запрещена; и
проста для совсем маленьких  детей,  осваивающих  "черепашью  графику"
языка программирования LOGO.  То,  что хорошие программы писались и на
языках, где рекурсия запрещена,  доказывает, что рекурсию всегда можно
свести к  итерационному  (циклическому) процессу для любых данных.  Но
каких затрат это зачастую стоит! Рекурсия очень эффективна и удобна:
   - при обработке деревьев;
   - при обработке дискретных изображений;
   - при   трансляции   программ   или   при   аналогичной   обработке
упорядоченных текстов (например, математических формул);
   - при  организации  сложных  процессов  в  объектно-ориентированных
средах.
   Однако рекурсия  тратит  много  памяти (при каждом вызове процедуры
надо разместить ее локальные переменные в стеке), поэтому она вредна:
   - в  сложных  объектно-ориентированных  средах,  где  иногда  время
прохождения событий по рекурсивным цепочкам сравнимо  со  временем  их
обработки конкретным объектом;
   - когда без нее можно очень просто обойтись (например, при работе с
рекуррентными формулами - см. также "типовые элементы алгоритмов").
   К последнему   случаю  как  раз  относится  вычисление  факториала,
которое часто приводят как иллюстрацию рекурсии:

   function f(x:real): real;
   begin
     if x<=1 then f:=1         {условие прерывания рекурсии}
             else f:=x*f(x-1); {углубление рекурсии}
   end;
   ...
   writeln(f(6));              {использование функции}

   Гораздо более  эффективный  способ  уже  рассматривался,  когда  мы
говорили о циклах - он настолько короток, что не грех привести его еще
раз:  "var i,n:integer; f:real; ... f:=1; for i:=2 to n do f:=f*i;"

   Покажем, как  можно  написать  рекурсивную  функцию  для вычисления
значения узла дерева формулы, рассмотренного в предыдущем разделе:

   function ExecNode(p:PNode):real;
   begin
     if p=nil then ExecNode:=0 else with p^ do case Operation of
       opConst: ExecNode:=Value;
       opAdd:   ExecNode:=ExecNode(arg1)+ExecNode(arg2); {вот и}
       opSub:   ExecNode:=ExecNode(arg1)-ExecNode(arg2); {рекурсия}
       opMul:   ExecNode:=ExecNode(arg1)*ExecNode(arg2);
       opDiv:   ExecNode:=ExecNode(arg1)/ExecNode(arg2);
       opSin:   ExecNode:=sin(ExecNode(arg));
       opCos:   ExecNode:=cos(ExecNode(arg));
       else     ExecNode:=0;
     end;
   end;

   Сравнительно легко будет написать также рекурсивные  процедуры  для
формирования такого  дерева в результате трансляции текста.  Для этого
упорядоченный текст,  каковым является формула, следует описать в виде
синтаксических диаграмм, какие используются при описании PASCAL'я. Это
очень удобное  средство   поможет   не   только   написать   процедуры
трансляции, но  и  отыскать  все  ошибки  в транслируемом тексте (если
очередной символ не совпадает с ожидаемым по синтаксической  диаграмме
- ошибка).

   Классический пример из области  обработки  изображений  -  закраска
замкнутого контура на дискретном рисунке (экране дисплея):

   var Picture:array[1..n,1..n] of char;  {массив для картинки}
     procedure Fill(x,y:integer); {закраска вокруг точки (x,y)}
     begin
       if Picture[x,y]<>'*'   {символ границы}
       then begin
         Picture[x,y]:='*';   {закрасить текущую точку}
         Fill(x+1,y);         {закрасить окружающие точки}
         Fill(x-1,y);
         Fill(x,y+1);
         Fill(x,y-1);
       end;
     end;
   ...
     Fill(11,15);  {начальная точка для закраски}

   Очень рекомендую   реализовать   такую   программу   хотя   бы   на
прямоугольнике символьного экрана размером 20x20  -  Вы  увидите,  как
работает этот алгоритм. Однако он имеет существенный недостаток - если
изображение большое (например, графический экран IBM PC 350x640 точек)
- никакого стека не хватит для столь глубокой рекурсии. В этих случаях
алгоритмы, разумеется, должны быть сложнее.


                   14. ОПЕРАЦИОННЫЕ СИСТЕМЫ  (2 ч)

   Напомним, что  операционная  система  (ОС) - это комплекс программ,
обеспечивающих функционирование ППО  на  конкретной  ЭВМ,  управляющих
ресурсами  ЭВМ  и обеспечивающих диалог с оператором.  Таковы основные
функции ОС.
   На ЭВМ   разного   типа  используются,  соответственно,  разные  ОС
(например, RT-11 или RSX-11 на СМ,  ДВК;  MS-DOS или MS-Windows на IBM
PC; DOS,  OS  или  VMS  на  ЕС  ЭВМ).  Делались также попытки создания
универсальных ОС  для  разных  ЭВМ  -  разных  внутри,  но  одинаковых
снаружи. Примером такой системы является UNIX, функционирующий на VAX,
IBM и ряде других ЭВМ;  но полной унификации все равно пока  нет  (как
нет в нашем распоряжении достаточно  мощной  ЭВМ  для  этой  "толстой"
системы).
   Познакомимся с  основными логическими понятиями операционных систем
(с их анализа следует всегда начинать изучение какой-либо новой ОС).

   1. ФАЙЛОВАЯ СИСТЕМА обеспечивает хранение информации на устройствах
внешней памяти и определяет механизм доступа к ним.
   Информация, как мы  уже  говорили,  хранится  на  дисках  и  других
устройствах в   виде   файлов.   Физически   файл  представляет  собой
заполненный информацией участок носителя (не обязательно непрерывный).
Логически (с  точки  зрения  ОС)  файл  представляет собой минимальную
порцию данных,  с которой ОС работает, как с единым целым. Каждый файл
имеет свое   имя,  зарегистрированное  в  каталоге  носителя.
   Каталог доступен оператору через командный язык ОС,  а программисту
- с   использованием   стандартных   подпрограмм   ОС.  Каталог  можно
просматривать, переименовывать файлы,  удалять их  или  копировать  на
новое место.
   Каталог может иметь сложную структуру с подкаталогами (директориями
в MS-DOS  и RSX-11 или виртуальными файловыми носителями - логическими
дисками в  RT-11).  В  каталоге  также   записывается   дополнительная
информация о файлах - их адреса на диске,  размеры,  атрибуты, время и
дата последней модификации.

   2. ДРАЙВЕРЫ ВНЕШНИХ УСТРОЙСТВ предназначены для управления внешними
устройствами. Они  представляют  собой  либо подпрограммы,  входящие в
состав монитора ОС,  либо отдельные программы,  запускаемые при старте
ОС или  по команде оператора.  Функция драйвера - принять от ОС запрос
на ввод/вывод данных на устройство и в соответствии  с  ним  правильно
организовать управление   устройством.  Драйверы  обычно  работают  по
прерываниям - они не работают до тех пор,  пока к ним не обратится  ОС
или пока  не произойдет какое-либо событие на обслуживаемом устройстве
(например,  оператор нажмет клавишу или передвинет мышь).  После этого
драйвер  активируется,  делает свое дело и снова уходит в ожидание,  а
прерванная программа продолжает выполнение.

   3. МОНИТОР   предназначен   для   управления   ресурсами  ЭВМ.  Это
специальная программа, которая загружается в ОЗУ при включении питания
(или уже прошита в ПЗУ) и выполняется постоянно. Монитор обеспечивает:
   - работу драйверов;
   - прием и обработку команд оператора;
   - в  многопользовательских  ОС  -  одновременную  работу нескольких
пользователей в   режиме   разделения   времени;   при   этом   каждый
пользователь работает  с  ЭВМ  так,  как  будто  он один,  и только по
характерным задержкам  в  работе  замечает,  что  одновременно  с  ним
работают другие.

   4. ИНТЕРФЕЙС  С  ОПЕРАТОРОМ  характерен  для  каждой ОС и позволяет
оператору совершать все необходимые  действия  для  работы  с  файлами
своих  программ  и данных,  выполнения программ и управления ресурсами
ЭВМ. В зависимости от способа  функционирования  ЭВМ  интерфейс  может
быть интерактивным (диалоговым),  что характерно для персональных ЭВМ.
В хороших ОС (MS-Windows,  Norton  Commander  для  MS-DOS)  он  хорошо
продуман,  удобен  и  не  требует  предварительного  изучения (команды
подаются путем нажатия кнопок);  в простейших и устаревших ОС (MS-DOS,
RT-11, RSX-11) реализован интерфейс типа "командный язык":
   - монитор ждет ввода команды;
   - оператор вводит команду в виде текстовой строки;
   - монитор выполняет команду и снова ждет.
   Разумеется, такой  интерфейс  требует  больших усилий по изучению и
запоминанию большого количества команд;  но  работа  на  ЭВМ  все  еще
остается диалоговой  -  всегда можно набрать команду справки (HELP или
просто "?") и изучить по ней команды системы.
   На больших  ЭВМ  реализуется  также  пакетный  режим работы,  когда
каждый пользователь  заранее  готовит  свою  задачу  к  выполнению   с
использованием специального  командного  языка  и кладет эту задачу во
входной поток вместе с десятками  и  сотнями  таких  же  задач  других
пользователей. После  этого  он  может  идти домой;  ночью его задача,
возможно, будет пропущена через ЭВМ, и утром он может взять распечатку
и увидеть там одну маленькую лишнюю запятую - и все сначала...

   5. УТИЛИТЫ, или СИСТЕМНЫЕ ПРОГРАММЫ ОБЩЕГО НАЗНАЧЕНИЯ предназначены
для выполнения отдельных  часто  используемых  операций  по  работе  с
программами,  данными и по обслуживанию внешних устройств (текстовые и
графические редакторы,  трансляторы,  программы обслуживания дисков  и
т.п.).  Кроме  собственно системных утилит,  к системному программному
обеспечению относят неисчислимое количество  программ,  способствующих
лучшему  выполнению  указанных  функций.
   Пустой дом, по определению, обеспечивает функции жилища, но человек
тащит  туда  нужную мебель,  не очень нужный телевизор и совершенно не
нужные (с точки зрения выживания) золото  и  хрусталь;  точно  так  же
каждый пользователь обживает свою ЭВМ.


             15. ТРАНСЛЯТОРЫ ЯЗЫКОВ ВЫСОКОГО УРОВНЯ (2 ч)

   Поскольку нашим  предметом  является прикладное программирование на
языках высокого уровня,  необходимо представлять процессы подготовки и
выполнения программ, обеспечиваемые ОС.
   Разумеется, программа,  написанная  на   языке   высокого   уровня,
абсолютно непонятна  ЭВМ  как  исполнителю.  Ее необходимо перевести в
команды процессора или  выполнять  с  помощью  специальной  программы.
Общее классообразующее  название  для  таких  программ  - трансляторы.
Трансляторы бывают двух принципиально разных видов - интерпретаторы  и
компиляторы (хотя ряд трансляторов сочетает оба этих подхода). В одной
и той  же  ОС  могут  быть  реализованы   как   компиляторы,   так   и
интерпретаторы одного и того же языка программирования.
   Интерпретаторы -  это  диалоговые  программы,   которые   позволяют
программисту работать   с   текстами   его   программ:  редактировать,
запускать на  выполнение  целиком   или   по   отдельным   операторам,
просматривать значения  переменных.  Интерпретатор  полностью хранит в
ОЗУ текст  программы,  а  при  выполнении  каждый  раз  переводит  его
буквально по буковке. Поскольку задача анализа текста довольно сложна,
интерпретаторы выполняют  программу  чрезвычайно  медленно   (основное
время тратится  не  на исполнение,  а на перевод).  Кроме того,  объем
интерпретируемой программы ограничен объемом свободного  ОЗУ,  которым
интерпретатор располагает.
   Компиляторы переводят программу один раз  и  позволяют  получить  в
конечном итоге  загрузочный  модуль  -  программу  в  машинных  кодах,
которую можно хранить на диске  и  запускать  на  выполнение  в  любой
нужный момент (уже без наличия самого транслятора, а иногда и даже без
операционной системы).  Скомпилированная  программа   выполняется   на
порядок быстрее,  чем  обрабатываемая интерпретатором,  но сам процесс
компиляции очень длителен,  поэтому отлаживать программу,  имея только
компилятор, трудно  и  долго.  Трудно  потому,  что  нельзя  исполнять
программу по шагам, смотреть значения переменных и т.п.
   Следовательно, для  первоначальной  отладки  программы  имеет смысл
использовать интерпретатор  (если  есть  такая   возможность),   затем
отлаженную программу    скомпилировать    компилятором    и   получить
законченный программный продукт в виде загрузочного модуля  (Из  этого
правила в  последнее  время появились хорошие исключения:  компиляторы
типа Turbo Pascal для IBM PC,  которые предоставляют  все  возможности
для отладки).
   Особая ситуация   складывается  на  маленьких  (бытовых)  ЭВМ.  Как
правило, они имеют небольшой объем ОЗУ,  а  использование  магнитофона
как единственного   носителя   информации  делает  невозможным  работу
компиляторов (для   них   обычно   необходим   диск).   Здесь    более
целесообразно использовать  интерпретатор (обычно он прошивается в ПЗУ
ЭВМ и является для нее стандартным программным обеспечением,  а заодно
и операционной  системой).  При  этом  почти  все  свободное ОЗУ может
занять текст программы  и  она  может  оказаться  достаточно  большой.
   Второй путь  для  бытовых  ЭВМ  гораздо менее прост и доступен.  Он
заключается в использовании большой ЭВМ  в  качестве  инструментальной
машины.  На  ней готовится и компилируется программа,  которая затем в
виде загрузочного модуля (машинных кодов) копируется  в  бытовую  ЭВМ.
Как  правило,  инструментальная  машина  не позволяет отлаживать такие
программы из-за различий в ОС,  а иногда  и  системы  команд,  поэтому
такой   путь   труден,   однако  является  единственно  возможным  при
программировании микропроцессоров,  что и  составляет  основные  будни
нашей специальности.
   Отметим, что для БК-0010 удобной инструментальной ЭВМ является  ДВК
(еще лучше в составе КУВТ) или УКНЦ, или БК-0011; для программирования
и отладки микропроцессорных контроллеров часто используется IBM PC.

                Виды генерируемого компиляторами кода

   Во-первых, нужно напомнить,  что  практически  любая  программа  на
языке высокого  уровня  явно  или  неявно  использует  ряд стандартных
(системных) подпрограмм  -  например,  для  вычисления  математических
функций и  даже для выполнения арифметических действий с вещественными
числами, если  процессор  ЭВМ  не  имеет  таких  команд.   Поэтому   в
загрузочном модуле  программы  всегда в том или ином виде присутствует
исполняющая система  транслятора  данного  языка.  В  зависимости   от
способа ее  использования получаемые компилятором коды подразделяют на
два основных типа: прямой или модульный ("шитый" код).
   Прямой код представляет собой последовательность машинных команд, в
которой при необходимости встречаются  команды  вызова  тех  или  иных
подпрограмм исполняющей системы.
   Шитый код вообще не содержит машинных  команд,  а  содержит  только
адреса (или номера) вызываемых подпрограмм исполняющей системы. В этом
случае в исполняющей системе  предусматриваются  подпрограммы  на  все
случаи жизни вплоть до элементарных операций копирования данных.
   Покажем, как  в  мнемонике  ассемблера  могут   выглядеть   участки
программы, реализующие простой оператор языка высокого уровня  c:=a+b,
где a и b - вещественные числа:

   ; прямой код компилятора PASCAL/RT-11}
   mov   a,-(sp)    ;передача параметров подпрограмме
   mov   a+2,-(sp)  ;сложения через стек
   mov   b,-(sp)
   mov   b+2,-(sp)
   call  $FADD      ;вызов подпрограммы сложения
   mov   (sp)+,c+2  ;копирование результата из стека в "c"
   mov   (sp)+,c

   ; модульный код компилятора FORTRAN/RT-11 (схематично)
   адрес подпрограммы копирования вещественного числа в стек;
   адрес вещественной переменной a
   адрес подпрограммы копирования вещественного числа в стек;
   адрес вещественной переменной b
   адрес подпрограммы вещественного сложения;
   адрес подпрограммы копирования вещественного числа из стека;
   адрес вещественной переменной c

   В первом  примере  каждая  команда занимает 4 байта,  а во втором -
каждый адрес занимает 2 байта (если подпрограмм у исполняющей  системы
не более 255,  то можно ссылаться не на адреса, а на порядковае номера
подпрограмм, занимающие по одному байту). Следовательно:
   - модульный код гораздо компактнее  прямого,  особенно  при  частом
использовании вещественной арифметики;
   - модульный код выполняется медленнее прямого  за  счет  постоянных
вызовов подпрограмм  и выходов из них (по сравнению со временем работы
подпрограмм вещественной арифметики это практически незаметно, поэтому
для больших расчетных задач модульный код предпочтительнее).
   Как и везде,  приходится искать разумный компромисс,  оценивая, что
важнее - объем кода программы или ее быстродействие.

   Для сравнения  покажем  "снимок"  процесса обработки того же самого
примера интерпретатором (с сокращениями):
   - читаем имя переменной до первого недопустимого символа ("с");
   - ищем в таблице переменных программы переменную "с";
   - берем из таблицы ее адрес;
   - читаем имя переменной ("a"), ищем в таблице и получаем адрес;
   - читаем знак операции ("+") и запоминаем его;
   - читаем имя переменной ("b"), ищем в таблице и получаем адрес;
   - в  соответствии  с  запомненным  кодом  операции  формируем  блок
параметров для подпрограммы вещественного сложения;
   - вызываем подпрограмму сложения;
   - копируем результат по запомненному адресу результата.

   Теперь Вас   не   удивит   таблица,   составленная   на   основании
экспериментов с простыми расчетными программами в ОС RT-11 (СМ-1420):
         ───────────┬─────────────┬─────────────────────────
         транслятор │ время счета │ размер кода
         ───────────┼─────────────┼─────────────────────────
         BASIC      │   50..200   │ размер исходного текста
         PASCAL     │         1   │ 4K + код * (3..5)
         FORTRAN    │ 0.01..0.5   │ 15К + код
         ───────────┴─────────────┴─────────────────────────
   Отсюда видно,  что  размер  исполняющей системы у FORTRAN'а больше,
чем у PASCAL'я,  но у последнего размеры кода растут гораздо  быстрее.
Таким образом,  для  больших  программ модульный код предпочтительнее.
Обратите внимание, что сравнение быстродействия программ на PASCAL'е и
FORTRAN'е противоречит  вышесказанному.  Все дело в том,  что PASCAL и
FORTRAN используют разные подпрограммы вещественной  арифметики,  и  у
FORTRAN'а они  гораздо  более  эффективные.  Минимальное  же  значение
времени счета относится к случаю,  когда FORTRAN генерирует  код  FPU,
использующий имеющийся в ЭВМ математический сопроцессор.

         Этапы подготовки компилируемых программ к выполнению

   Этот вопрос   заслуживает   отдельной   темы,  так  как  имеет  ряд
характерных особенностей, общих для всех ОС.
           ┌────────────────┐
           │ исходный текст │
           └────────┬───────┘
         компиляция │
           ┌────────┴─────────┐      ┌──────────────────┐
           │ объектный модуль │  ... │ объектный модуль │
           └────────┬─────────┘      └────────┬─────────┘
         компоновка │      ┌──────────────────┘
           ┌────────┴──────┴────┐
           │ загрузочный модуль │
           └────────────────────┘
   В результате  компиляции  получается  объектный  код,  который   от
полного машинного кода отличается тем,  что в нем не определены адреса
внешних подпрограмм.  Внешние подпрограммы (см. раздел "ПОДПРОГРАММЫ")
могут храниться  на  диске  в  виде  файлов  объектных модулей и иметь
следующее происхождение:
   - стандартные подпрограммы  из  системной объектной  библиотеки;
   - ранее   скомпилированные   Вами   модули    с    вспомогательными
подпрограммами;
   - приобретенные Вами  отдельно  библиотеки  прикладных  подпрограмм
(математических, графических, музыкальных и пр.).
   На следующем этапе - компоновки  -  используемые  объектные  модули
объединяются в  один  загрузочный,  который  уже  содержит полноценный
машинный код - его можно загружать в ОЗУ и запускать на исполнение.
   Таким образом,   введение   промежуточного  продукта  компиляции  -
объектного кода дало полезные возможности:
   - заблаговременной подготовки вспомогательных подпрограмм;
   - создания библиотек вспомогательных подпрограмм и их использования
разными программами;
   - возможность продажи библиотек  прикладных  подпрограмм,  исходный
текст которых скрыт от потребителя (один из простейших способов охраны
авторских прав на программный продукт).
   Иногда (например,  в коротком PASCAL/RT-11) в результате компиляции
генерируется   код   на  ассемблере,  и  только  после  обработки  его
ассемблером получается объектный  код.  Это,  в  частности,  позволяет
применять в программе на PASCAL'е ассемблерные вставки.


    16. ОПЕРАЦИОННЫЕ СИСТЕМЫ ТИПА RT-11 ДЛЯ МИНИ- И МИКРОЭВМ (2 ч)

   Речь пойдет о DEC-освских ЭВМ (имеющих систему команд,  совместимую
с ЭВМ PDP-11 фирмы Digital Equipment Corporation).  Это миниЭВМ  СМ-4,
СМ-1420, микроЭВМ Электроника-60,  ДВК,  БК-0010,  БК-0011, УКНЦ. Если
первые применяются обычно в вузах,  в системах АСУП,  в  качестве  АРМ
инженера-проектировщика,   то   Электроника-60  широко  применяется  в
промышленности в устройствах ЧПУ. Даже бытовой компьютер БК-0010 очень
удобен  в  качестве  дешевой  управляющей  ЭВМ  -  где  его  только не
используют!
   Для этих ЭВМ создано два основных класса ОС:
   - RSX-11 для  многотерминальной  работы  на  миниЭВМ  (в  советском
варианте она называется ОС РВ);
   - RT-11 как наиболее простая ОС для микроЭВМ (в советском  варианте
как только не обзывали ее разновидности - РАФОС, ФОДОС, ОС ДВК).
   К сожалению,  эти ОС  не  совместимы  друг  с  другом  по  файловой
системе. Файловая  система  RSX-11 больше похожа на MS-DOS для IBM PC.
Поскольку на нашей кафедре  RSX-11  не  эксплуатируется,  мы  подробно
остановимся на разновидностях ОС RT-11. Тем более, что она работает на
ДВК и других микроЭВМ.

   1. Файловая  система.   На   каждом   диске   имеется   каталог   и
располагаются файлы.  Поскольку RT-11 можно считать простейшей ОС, все
файлы в ней непрерывны (в то время как в MS-DOS и в RSX-11 файлы могут
располагаться кусочками  в  разных  местах  диска,  в  зависимости  от
наличия свободных участков диска).  Непрерывность файлов ведет к тому,
что при  интенсивной  работе  с  диском (ряд файлов удаляется,  другие
создаются заново)  на  диске  образуется  множество  дыр  -  свободных
участков, в  которые  очередной  файл  уже  не уместится (хотя в сумме
свободного места вполне достаточно).  Поэтому  периодически  требуется
выполнять процедуру сжатия дисков.
   RT-11 поддерживает также логические диски  -  внутри  какого-нибудь
файла может  быть  организован  так  называемый  виртуальный  файловый
носитель со своим каталогом. Опять же, если в MS-DOS или RSX-11 размер
подкаталогов (директорий)  ничем не ограничивается,  то в RT-11 размер
логического диска фиксирован.
   Полное имя  файла  включает в себя название устройства,  собственно
имя файла и его расширение: "DEV:FILNAM.TYP". Имя файла может включать
6 символов, расширение - 3. В RT-11 предусмотрены устройства:
   RK: - жесткий диск на  СМ-4,  СМ-1420.  Дисков  в  ЭВМ  может  быть
несколько, тогда они нумеруются, начиная с 0: RK0, RK1 и т д.
   DW: - винчестер на ДВК;
   MX: -  200К  40-дорожечный или 400К 80-дорожечный дисковод для 5"25
дискет на ДВК;
   MY: - 800К 80-дорожечный дисковод на ДВК;
   MZ: - 400К 40-дорожечный дисковод на УКНЦ  (MY и MZ совместимы друг
с другом,  их дискеты можно также прочитать на IBM PC; а вот MX вообще
ни с чем не совместим);
   TT: - терминал (дисплей для вывода, клавиатура для ввода);
   LP: - печатающее устройство;
   MT: - цифровой магнитофон;
   PC: - перфолента;
   LD: - логический диск;
   SY: - системный диск;
   DK: - текущий диск (если  в  имени  файла  не  указано  устройство,
подразумевается DK:).
   Стандартные расширения имен файлов:
   .PAS - программа на PASCAL'е;
   .MAC - программа на макроассемблере;
   .FOR - программа на FORTRAN'е;
   .OBJ - объектный модуль;
   .SAV - загрузочный модуль;
   .BAS - программа на BASIC'е;
   .DAT - файл данных;
   .LST - текстовый файл для вывода на печать;
   .SYS - системный файл (монитор, драйвер);
   .COM - командный  файл  (для  описания  процедур  вроде  трансляции
PASCAL-программ, требующих несколько команд);
   .BAK -  старая версия файла (например,  после редактирования текста
старый его  вариант  переименовывается  в  .BAK,  чтобы   можно   было
восстановить его при необходимости).

   2. Драйверы  внешних устройств выделены в RT-11 наиболее явно.  Они
размещаются в  отдельных  файлах  с  расширением .SYS ,  и для задания
конфигурации ОС нужно иметь на системном диске  необходимые  драйверы.
Имя файлов  драйверов  совпадает с именем обслуживаемых ими устройств,
например, MX.SYS,  LD.SYS.  Все  драйверы  работают  одинаково  -  они
принимают от   монитора  ОС  (или  формируют)  стандартный  запрос  на
ввод/вывод. Благодаря    высокой    степени    унификации     драйверы
взаимозаменяемы: можно,   например,   переназначить   поток  вывода  с
печатающего устройства на терминал (командой .ASSIGN TT: LP:).

   3. Монитор  (ядро  ОС).  Их  предусмотрено  несколько  для   разных
способов управления ЭВМ:
   RT11RM.SYS - простейший монитор для управляющих ЭВМ;
   RT11SJ.SYS - однозадачный (Single Job) монитор для однотерминальной
работы (основной монитор для работы на ДВК);
   RT11FB.SYS -  фоново-оперативный  (Foreground-Background)  монитор,
позволяющий запускать несколько программ в фоновом режиме;
   RT11XM.SYS -  монитор,  позволяющий использовать расширенную память
(eXtended Memory) сверх основных 64К ОЗУ;
   RT11TS.SYS -   многотерминальный  монитор  для  миниЭВМ  (на  нашей
СМ-1420 функционирует его аналог - ОС NTS).

   4. Интерфейс с оператором - командный. Монитор выдает приглашение к
вводу команды  и ждет ввода команды;  обрабатывает введенную команду и
вновь ожидает ввода команды.  Основные команды  RT-11  (команды  можно
сокращать до  такого количества символов,  чтобы они еще были отличимы
друг от друга):
   HELP - получение справочной информации по командам ОС;
   DIR - просмотр каталога, например:
      DIR MX1: - каталог MX1:;
      DIR *.PAS - вывести информацию о всех файлах текущего каталога с
расширением .PAS;
      DIR NAME%.PAS - посмотреть все файлы вида  NAME1.PAS,  NAMEB.PAS
(символ * означает любое количество символов, а % - один символ);
      DIR/FREE - посмотреть свободные места на диске;
      DIR/SORT:DATE - вывести каталог,  упорядоченный по дате создания
файлов;
   COPY <откуда> <куда> - копирование файлов, например:
      COPY MX0:*.* MX1:*.* - скопировать все файлы с диска MX0 на MX1;
   RENAME <что> <во что> - переименование файлов;
   DELETE <список файлов> - удаление файлов;
   SHOW MEMORY - показать распределение памяти;
   SHOW S - показать смонтированные логические диски;
   RUN <имя> - запуск программы на выполнение;
   R <имя> - запуск программы с системного диска;
   OFF - конец работы с системой (для TS, NTS);
   ASSIGN <что> <на что> - переназначение устройств, например:
      AS TT  LP  -  отправить  на терминал  поток  вывода  печатающего
устройства;
      AS LD1 DK - сделать текущим логический диск LD1;
   DEASSIGN <устройство> - отменить назначение устройства, например:
      DEAS LP - восстановить поток вывода на печатающее устройство.
   SQ <устройство> - сжатие диска;
   @<имя> - выполнение указанного командного файла.

   5. Системные программы общего назначения:
   - системные утилиты, выполняющие некоторые команды ОС:
     PIP - программа копирования, переименования и удаления файлов;
     DIR - программа распечатки каталогов;
     DUP - программа инициализации каталогов и сжатия дисков;
   - редакторы текстов (EDIV, SED, USED, EDIK);
   - программы, обеспечивающие:
     MACRO - трансляцию программ на ассемблере;
     PASCAL - трансляцию программ на PASCAL'e;
     LINK - компоновку;
     LIBR - работу с объектными библиотеками.

           Порядок трансляции программ на PASCAL'е в RT-11

   В RT-11 есть два компилятора PASCAL:
   - "длинный" - мощный транслятор,  отлавливающий даже неиспользуемые
переменные, но требующий на диске как  минимум  500  свободных  блоков
даже для трансляции коротеньких программ);
   - "короткий"  -  простой  транслятор,  выдающий  код  программы  на
ассемблере. Порядок работы с ним следующий:

   R PASCAL                 - вызов транслятора
   <имя>,TT:=<имя>          - командная строка транслятору
   ^C                       - окончание работы с транслятором
   MACRO <имя>              - трансляция ассемблерного текста
   LINK <имя>,SY:PASCAL     - компоновка программы с библиотекой
                              стандартных подпрограмм PASCAL
   DEL <имя>.(MAC,OBJ)      - удаление промежуточных файлов
   SIZE <имя>/D:n           - задание n килобайт памяти для
                              переменных программы.
   RUN <имя>                - запуск программы на выполнение.

                          Редакторы текстов

   Программное и аппаратное обеспечение ЭВМ типа PDP-11 использует  не
8-битную, а  7-битную  кодировку  символов (КОИ-7),  где последний бит
используется для контроля четности -  проверки  правильности  передачи
данных. Поэтому   на   таких   ЭВМ  недоступны  символы  псевдографики
("┌┬┐╔╦╗"),  а также символы русского алфавита,  находящиеся в верхней
части  кодовой  таблицы.  Поэтому  в  том  оборудовании,  которое не в
состоянии  показывать  полную  таблицу  кодов,   символы   с   кодами,
соответствующими в  ASCII строчным латинским буквам,  отображаются как
заглавные русские (соответственно,  такие дисплеи  и  тексты  для  них
называют двухрегистровыми).  Четырехрегистровыми  называются тексты (и
аппаратура),  где используются  латинские  (строчные  и  заглавные)  и
русские  (строчные  и  заглавные) символы.  Поскольку их кодировка все
равно  остается  7-битной,  приходится  для   переключения   регистров
русский/латинский использовать специальные управляющие коды (14 и 15 в
десятичной системе).  При переносе таких текстов на IBM PC (а также на
БК-0010),   где   используется   полный  8-битный  код,  требуется  их
перекодировка.
   Немного о  редакторах текстов.  Молодое поколение пользователей ЭВМ
не представляет никаких редакторов текстов,  кроме экранных.  Мне  еще
приходилось работать  со  строчным редактором текстов (EDIT),  который
предназначался для терминалов типа электрическая пишущая машинка. Идея
о том,  что  можно  часть редактируемого текста показывать на экране и
при его   редактировании   использовать    клавиши    со    стрелками,
психологически была  совершенно  новой,  и  мы,  как дети,  радовались
первым экранным редакторам для RT-11 SED  и  USED.  Четырехрегистровые
редакторы появились  только  на  ЭВМ  типа ДВК.  И по сегодняшний день
наиболее удачным,  эргономичным и очень маленьким по размерам я считаю
редактор И.Ныса  EDIK.  Даже  на IBM PC меня долгое время преследовала
ностальгия по этому редактору.
   При редактировании 4-регистровых текстов возникает проблема  -  код
переключения на  латинский  регистр  в  ОС  означает  отмену вывода на
дисплей. Приходится некоторым образом  модифицировать  ОС  при  помощи
специальных программ.
                         Управление дисплеем

   В большинстве    миниЭВМ   рассматриваемого   класса   используются
алфавитно-цифровые терминалы,  совместимые   по   системе   команд   с
терминалами  VTA-52  или  Электроника  15-ИЭ-013.  Для  их  управления
используются управляющие последовательности кодов,  посылку которых на
дисплей можно вынести в процедуры:

   procedure ClrScr; {очистка экрана}
   begin
     write(chr(33b),chr(110b)); {курсор в начало экрана}
     write(chr(33b),chr(112b)); {очистка экрана под курсором}
   end;

   procedure Cursor(Line,Column:integer); {установка курсора}
   begin
     write(chr(33b),chr(131b),chr(Line+40b),chr(Column+40b));
   end;

   procedure ClrEoLn(Line,Column:integer); {очистка конца строки}
   begin
     Cursor(Line,Column);
     write(chr(33b),chr(113b));
   end;

                         Работа с клавиатурой

   ОС воспринимает стандартные управляющие коды:
   СУ/Ц - прерывание работающей программы (двукратное нажатие);
   СУ/В,СУ/Ц - прерывание программы в ОС NTS;
   СУ/С - приостановить вывод на дисплей;
   СУ/Я - возобновить вывод на дисплей;
   СУ/О - отменить/восстановить вывод на дисплей.

   Написание процедур   работы  с  клавиатурой  требует  использования
ассемблерных вставок и  использования  специальных  знаний  о  режимах
работы ОС,  поэтому предлагаются готовые процедуры из моей  библиотеки
ALLIB, доступные на нашей СМ-1420. Для этого требуется описать их:

   procedure TTspec; fortran; {отмена эхо-печати и ожидания при вводе}
   procedure TTnorm; fortran; {восстановление стандартного режима}
   function ITT:integer; fortran; {возвращает код символа без
     ожидания; если символа не было, возвращает -1}
   function ITTW:integer; fortran; {ожидает   ввода   символа   (без
     эхо-печати)}

и прикомпоновать библиотеку к загрузочному модулю:

   LINK <имя программы>,AL:ALLIB,SY:PASCAL

   Коды некоторых клавиш (в восьмеричной системе):
   33,101 - стрелка вверх;
   33,102 - стрелка вниз;
   33,103 - стрелка вправо;
   33,104 - стрелка влево;


                 17. ФОРМЫ ОРГАНИЗАЦИИ ДИАЛОГА (2 ч)

   Фирмой IBM   предложен   стандарт   на   систему   пользовательских
интерфейсов  (CUA,  Common  User  Access) [11]. Основные  требования к
программе таковы: она не должна вызывать стресс из-за неудобства ввода
данных    или    из-за   боязни   совершить   неправильное   действие.
Следовательно, обязательно надо предусмотреть:
   - организацию  ввода  данных  в  виде  экранной  формы  (аналогично
заполняемым бумажным  бланкам),  где  вся порция вводимых данных будет
перед глазами,  ее можно десять раз проверить и исправить,  прежде чем
отправиться дальше;
   - предусмотреть  минимально  необходимую  подсказку  и  развернутую
справочную информацию по требованию оператора (Help);
   - не позволять оператору случайно  совершить  необратимые  действия
(удаление данных и пр.),  переспросить при необходимости, уверен ли он
в том, что собирается делать (т.н. "защита от дурака" - "foolproof");
   - предоставить  оператору  возможность вернуться на предыдущие шаги
для исправления возможных ошибок;
   - чтобы  оператор  не  чувствовал  себя  "не в своей тарелке",  все
диалоговые формы должны соответствовать функционально  и  по  внешнему
виду упомянутому стандарту CUA;
   - по  тем  же причинам желательно использовать ставшие стандартными
де-факто назначения функциональных клавиш: F1 - для получения справки,
F2 -  для записи,  F3 - для чтения рабочего файла,  F10 - для выхода в
основное меню, Alt-X для выхода из программы и др.
   Выполнению этих  требований способствует применение интегрированных
сред для разработки программ,  например,  библиотеки Turbo Vision  для
Turbo Pascal  и  для  Turbo C++ фирмы Borland International,  inc (для
работы в текстовом режиме  дисплея)  и  моей  аналогичной  библиотекой
Graphic Vision для работы в графическом режиме дисплея. В операционной
системе Windows   также   предусмотрены   специальные   средства   для
диалогового построения интерфейса - Object Vision и др.

   Теперь познакомимся  с  конкретными  формами,   используемыми   при
организации диалога.

   1. ВОПРОС-ОТВЕТ  -  самая  простая  форма диалога,  когда программа
о чем-то спрашивает оператора или просит его ввести какие-либо данные.
В принципе,   эта  форма  позволяет  описать  сценарий  диалога  любой
сложности, но  при  наличии  большого  выбора  вариантов  нужно  будет
задавать слишком  много вопросов,  и это очень неудобно для оператора.
Ухудшает эту ситуацию и то,  что программа ведет оператора по  длинной
цепочке, а  человек  не  в  состоянии  держать  ее  в голове.  Поэтому
нарастает, например,  стресс из-за боязни сделать  ошибку.  Во-первых,
оператор не  всегда  знает,  можно  ли  ее  потом  будет исправить,  а
во-вторых, когда он дойдет до вопроса о контрольной проверке, он почти
наверняка забудет, какие и где он сделал ошибки.
   Стандартом CUA  предусмотрено  применение  вопросов  для   краткого
сообщения об  ошибках  и  для  уточнения  и  подтверждения  каких-либо
действий. Но даже такой простой вопрос должен  быть  оформлен  в  виде
всплывающего (pop-up)   окна   с   одной   или  несколькими  кнопками.
Разумеется, удовлетворить такие требования можно только при достаточно
высоком уровне программирования.

   2. КОМАНДНЫЙ  РЕЖИМ  работы   -  можно  сказать,  устаревшая  форма
диалога, оставшаяся в наследство от терминалов  в  виде  электрических
пишущих машинок.    Командный   режим   характерен   для   большинства
операционных систем и подробно описан в соответствующих  разделах.  Но
90% пользователей IBM PC,  где  стоит  MS-DOS,  ее  командным  режимом
вообще не  пользуются.  Потому  что  есть  удобные диалоговые оболочки
(наиболее распространен Norton Commander),  позволяющие одним нажатием
клавиши или мыши выполнить ту или иную команду ОС.
   Командный режим    может    оставаться    близким   сердцу   многих
профессиональных программистов, но и они после привыкания к диалоговым
оболочкам изменяют    мнение   и   предпочитают   их   из-за   большей
эргономичности. А для новичков командный режим  есть  верное  средство
сразу и навсегда отбить охоту работать с такой программой (даже если в
ней предусмотрена справка по ее командам).

   3. МЕНЮ  является  более  прогрессивной формой организации диалога.
Простейшее меню Вы видите,  работая на нашей СМ-1420. Оно подсказывает
оператору список возможных в данной ситуации действий и просит, введя,
например, номер  показанного  действия,  выбрать   его.   Такое   меню
реализуется почти так же просто,  как и "вопрос-ответ" - надо очистить
экран, распечатать меню, ввести ответ и, проверив его на допустимость,
открыть большой  оператор CASE,  где перечислить действия программы на
любой ответ оператора.
   В программах  на  IBM  PC  распространены  более сложные виды меню:
выталкиваемые или падающие (pull-down) и всплывающие (pop-up).

  ▓▓Пункт 1▓▓Пункт 2▓▓Пункт 3▓▓▓▓▓▓▓▓▓▓▓▓Основное меню▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ░░░░░░░░░░│Подпункт 1│ Падающие меню ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  ░░░░░░░░░░│Подпункт 2│█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  ░░░░┌■────│Под│Подпункт 1│─────────────┐░░░░░ Рабочий стол ░░░░░░░
  ░░░░│     └───│Подпункт 2│█            │█░░░░░ (Desktop) ░░░░░░░░░
  ░░░░│      ▀▀▀│Подпункт 3│█      ┌─────────┐░░░░░░░░░░░░░░░░░░░░░░
  ░░░░│         └──────────┘█      │ Пункт 1 │█░░░░░░░░░░░░░░░░░░░░░
  ░░░░│          ▀▀▀▀▀▀▀▀▀▀▀▀      │ Пункт 2 │█░ Всплывающее ░░░░░░░
  ░░░░│   Рабочее окно             │ Пункт 3 │█░░░ меню ░░░░░░░░░░░░
  ░░░░│   (Window)                 └─────────┘█░░░░░░░░░░░░░░░░░░░░░
  ░░░░│                             ▀▀▀▀▀▀█▀▀▀▀░░░░░░░░░░░░░░░░░░░░░
  ░░░░└──────────────────────────────────┘█░░░░░░░░░░░░░░░░░░░░░░░░░
  ░░░░░▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀░░░░░░░░░░░░░░░░░░░░░░░░░
  ▓▓F1-Help▓▓▓▓▓▓▓▓▓▓Статус-строка▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓

   Падающие меню  мы  видим в Norton Commander'/е,  в Turbo Pascal'е и
других программных продуктах на IBM PC.  Основная его часть  -  строка
меню, помещаемая в верхней части экрана. При выборе какого-либо пункта
с клавиатуры или  с  мышью  под  заголовком  этого  пункта  появляется
прямоугольник  с  меню,  в котором перечислены подпункты первоначально
выбранного пункта.  Каждый  подпункт,  в  свою  очередь,  также  может
содержать несколько подпунктов.
   Основную строку меню  мы  видим  перед  глазами  постоянно,  и  она
доступна практически  во всех режимах программы,  поэтому используется
оно в основном для общих команд управления программой.
   В тех же продуктах встречаются всплывающие меню - они  возникают  в
центре экрана и исторически не обязательно связаны  со  строкой  меню.
Такие меню   применяются   при  возникновении  каких-либо  ситуаций  в
программе, когда  требуется  что-то  уточнить  или  выбрать  из   ряда
предлагаемых возможностей.
   Для того, чтобы запрограммировать такие достаточно сложные объекты,
как меню,  да  еще  и  обеспечить  их реакцию на нажатие и перемещение
мыши, используются различные библиотеки подпрограмм,  например,  Turbo
Professional фирмы Turbo Power Software,  или Turbo Vision, входящая в
состав Turbo Pascal версий 6.0 или 7.0.  Для обоих программ существуют
программы для автоматизации программирования меню и других  диалоговых
объектов  -  MenuMaker и Turbo Vision Development Toolkit (а также мой
Resource Editor для Graphic Vision).
   Следует отметить,  что  вынесение  всех  основных команд в падающее
меню удобно для  новичков,  но  для  интенсивно  работающих  с  данной
программой становится обузой,  так как требует пусть пару-другую, но -
лишних действий.   Поэтому   особо   употребительные   команды    меню
обрабатывает и  при  нажатии  указанной  в  их  подпунктах  комбинации
клавиш.   Два-три   раза   воспользовавшись   меню,   оператор   чисто
автоматически запоминает  соответствующие комбинации клавиш и начинает
работать более эффективно.

   4. СТАТУС-СТРОКА также предусмотрена стандартом CUA  и  наблюдается
почти во  всех  программах  на  IBM  PC,  располагаясь  в нижней части
экрана. Она выполняет две основные функции:  информировать оператора о
текущем режиме  программы  и подсказывать ему самые важные доступные в
этом режиме команды (их можно выбрать,  нажав  указанную  клавишу  или
указав нужную часть статус-строки мышью.
   В отличие от главного меню,  обычно не  меняющегося  на  протяжении
всей программы,  статус-строка меняется постоянно.  Обычно при запуске
любой программы мы видим в ней две команды, позволяющих самому темному
новичку познакомиться с программой: F1-Help и F10-Menu.

 ╔Отмена═════ Заголовок окна ═══════════════════════════════════════╗
 ║   Поле одновариантного выбора    Поле ввода текста, числа и т.п. ║
 ║   ┌───────────────────────┐      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒              ║
 ║   │ ( ) альтернатива 1    │                                      ║
 ║   │ (*) альтернатива 2    │       Поле многовариантного выбора   ║
 ║   │     ...               │       ┌─────────────────────┐        ║
 ║   │ ( ) альтернатива N    │       │ [ ] возможность 1   │        ║
 ║   └───────────────────────┘       │ [X] возможность 2   │        ║
 ║                                   │     ...             │        ║
 ║   Длинный список (меню)           │ [X] возможность N   │        ║
 ║   ┌───────────────────┐          └─────────────────────┘        ║
 ║   │                   │░                                         ║
 ║   │                   │▓-бегунок                                 ║
 ║   │ выбранная строка  │░                 ┌────────               ║
 ║   │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│░-полоса          │ Кнопка █              ║
 ║   │                   │░ скроллинга      └▄▄▄▄▄▄▄▄█              ║
 ║   └───────────────────┘                                         ║
 ╚══════════════════════════════════════════════════════════════════╝

   5. ЭКРАННЫЕ ФОРМЫ являются наиболее удачным для  человека  способом
диалога, учитывающим  его  психологические  особенности - лучше всего,
когда вся обрабатываемая информация находится перед его глазами,  и  в
возможно более наглядной форме (совсем хорошо,  если в виде картинок -
пиктограмм). Тогда человек может  многократно  проверить  и  исправить
все, что перед ним, и лишь затем отправить эту информацию в программу.
   Экранные формы имеют вид всплывающих диалоговых окон с  несколькими
полями ввода  и  редактирования  данных.  Они  похожи  на  стандартные
бумажные бланки,  которые мы  частенько  заполняем  в  разных  местах.
Например, диалоговое  окно используется для введения приходного ордера
в электронной бухгалтерии, или для оформления накладной, или для ввода
карточки регистрации  клиента  банка  или больницы - то есть для ввода
определенной законченной порции информации.
   Стандарт CUA  требует  легкости перемещения между отдельными полями
ввода в диалоговом  окне - указанием  их  мышью,  клавишами  <Tab>  (к
следующему) и  <Shift-Tab>  (к предыдущему),  а также по возможности -
просто стрелками.   Предусматривается   ряд   стандартных    элементов
диалоговых окон и типов полей ввода данных.
   Одновариантный выбор  используется  аналогично  меню.  Если  список
возможных альтернатив    слишком   длинный,   организуется   скроллинг
(листание) этого списка с помощью полосы скроллинга (Scroll  Bar),  на
которой имеется  бегунок,  приблизительно показывающий место видимой в
окошке части списка относительно всего списка;  и  кнопки  листания  в
одну и в другую сторону.
   Многовариантный выбор используется для установки  флагов,  задающих
те или иные возможности по управлению программой и т.п.  Можно было бы
вместо этого   поставить   просто  кучу переключателей типа "включено-
выключено", но   блок  многовариантного  выбора  позволяет  объединить
логически близкие переключатели.
   Кнопки используются  для  таких  стандартных операций,  как OK (все
готово, все  в  порядке),  Cancel  (отменить  диалог  без   сохранения
сделанных изменений),  ответов  на  вопросы  типа  Yes/No и каких-либо
специфических команд вроде "удалить", "открыть файл" и т.п.

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


     18. КРАТКОЕ ЗНАКОМСТВО С ПЕРСОНАЛЬНЫМИ ЭВМ ТИПА IBM PC (4 ч)

   Следует ожидать,  что наиболее популярной ЭВМ в  ближайшем  будущем
будут те  или иные модификации IBM PC.  Даже если вскоре будет создана
гораздо более  эффективная  ЭВМ,  горы  наработанного   для   IBM   PC
программного обеспечения  должны быть переведены тем или иным способом
на эту ЭВМ, иначе она не завоюет столь широкого круга потребителей.
   Сейчас имеется большое количество литературы по IBM  PC,  например,
[4] (я  включил  ее в список литературы не потому,  что она лучшая,  а
просто под руку попалась) - выбор вполне достаточный, но, к сожалению,
не столько в библиотеках, сколько в торговле.

                        Немного о самой IBM PC

   Области применения персональных ЭВМ класса IBM PC:
   - АРМ секретаря: подготовка, печать и рассылка текстовых документов
(деловых писем,   реклам   и   пр.);  осуществление  функций  связи  в
компьютерных сетях;
   - бухгалтерия   на   небольших   фирмах   и   другие  экономические
приложения;
   - АРМ  конструктора-проектировщика  в  различных  отраслях  науки и
техники;
   - автоматизация научного эксперимента и управление технологическими
процессами;
   - компьютерные игры.
   Для западных высших учебных  заведений  IBM  PC  считается  слишком
слабой ЭВМ,  там  широко используются более мощные Apple Macintosh'и и
многотерминальные VAX'ы.
   На IBM  PC  эксплуатируются в основном две ОС:  DOS и Windows фирмы
Microsoft Corporation.  MS-DOS требует минимальных ресурсов ЭВМ и  так
же  мало  дает  пользователю.  MS-Windows  для  более-менее нормальной
работы требует AT-386 с 4М ОЗУ,  а программы под Windows иногда и того
больше. Поэтому  многим она не по карману,  и все ведущие разработчики
пока делают свои продукты под обе эти ОС,  расчитывая,  что они  долго
еще будут мирно сосуществовать.

                          Клавиатура IBM PC

   О ней имеет смысл говорить,  поскольку она стандартная.  Клавиатура
включает поле алфавитно-цифровых клавиш (американский стандарт пишущей
машинки - QWERTY-клавиатура), клавиши управления курсором: Up (вверх),
Down (вниз), Left (влево), Right (вправо), Home (в начало строки), End
(в конец строки),  PageUp (на страницу вверх),  PageDown (на  страницу
вниз); блок цифровых клавиш генерирует коды цифр при включенном режиме
Num Lock  и  коды  управления  курсором  при  выключенном;   Backspace
(удаление предыдущего символа);  Delete (удаление следующего символа);
Insert (переключение режима ввода текста вставка/замена).
   Регистровые клавиши:  Shift (заглавные/строчные буквы),  Ctrl и Alt
для генерации различных управляющих кодов,  Caps Lock (фиксация режима
заглавных букв).
   Функциональные клавиши: Enter (перевод строки), Tab (горизонтальная
табуляция в  текстовых  редакторах  и  переход от одного окна или поля
ввода к другому в диалоговых средах),  Esc (отмена диалога,  выход  из
текущего режима),   F1-F12  (специальные  функциональные  клавиши  для
пользовательских программ,  см. также предыдущий раздел); Print Screen
(обычно в  соответствии  со  своим  названием  используется для печати
копии экрана на  принтере);  Pause  (временное  запрещение  вывода  на
дисплей; продолжение по любой другой клавише).
   Управляющие комбинации  клавиш:  Ctrl+C,   Ctrl+Break   (прерывание
текущего процесса, программы); Ctrl+Alt+Delete (перезагрузка ОС).

                     Операционная система MS-DOS

   В настоящее время эта ОС безнадежно устарела,  но  практически  все
владельцы  IBM  PC  поневоле  эксплуатируют  эту  ОС.  Сейчас  MS-DOS,
поскольку  она   имеет   встроенные   драйверы   дисковых   устройств,
используется  как  базовая  часть системного программного обеспечения.
Для любого ее  расширения  используются  внешние  (не  всегда  родные)
драйверы   и   программы,   нередко   по   сложности   многократно  ее
превосходящие.

   1. Файловая система обслуживает гибкие диски А:  (обычно 5"25),  B:
(обычно 3"5)  и  жесткий диск типа винчестер С:.  Последний может быть
при форматировании разбит на несколько частей (всего для  наименования
физических и логических дисков можно использовать буквы от D до Z).
   Структура каталогов   -   иерархическая.   Подкаталоги   называются
директориями, размеры   их   не   ограничиваются.  Файлы  не  являются
непрерывными - используются  все  периодически  возникающие  свободные
участки диска.  Поэтому,  в  отличие от RT-11,  пока на диске остается
свободное место,  на нем можно работать теоретически бесконечно долго.
На практике  сильная  фрагментация  файлов ведет к замедлению работы с
ними и к увеличению возможности случайного  сбоя  механики  дисковода.
Во избежание   этих  негативных  последствий  желательно  периодически
выполнять процедуру оптимизации диска (например, программой Speedisk).
   В имени файла можно указывать полный путь (path) к нему:

      полный путь    расширение          каталог
        ┌─────┐      ┌─┐                 ┌───┐
        C:\DOS\HIMEM.SYS              D:\GAME\WOLF\WOLF3D.EXE
        └─┘    └───┘                          └───┘
     устройство  имя (до 8 символов)          подкаталог

   Назначение расширения имени файла аналогично RT-11, как и ряд самих
расширений (.dat, .pas, .for, .lst, .txt и др.); а вот новые:
   .com - загрузочные модули размером менее 64К;
   .exe - загрузочные модули размером больше 64К;
   .bat - командные файлы (аналогично .com в RT-11).
   Если Вы хотите запустить новую программу,  где очень много  файлов,
ищите сначала  файл  READ.ME  или  README.*,  где  даны самые основные
сведения о ней.  Если нет,  то ищите что-либо вроде RUNME.BAT -  автор
наверняка лучше  Вас  знает,  как  сложно  работать с его программой и
поэтому написал командный файл. Если и его нет, ищите *.EXE (небольшие
файлы *.COM  могут  быть  вспомогательными;  хотя  иногда может быть и
наоборот). Если после таких упражнений у Вас в ЭВМ завелись вирусы, то
а) не  удивляйтесь - Вы не соблюдали элементарных мер предосторожности
и вообще наказаны за использование ворованной программы; б) побейте за
все это  того,  кто  Вам  принес  эту  программу;  в)  выключите ЭВМ и
позовите системного программиста.  Может быть,  он и не сможет  ничего
сделать, но бодрящая взбучка Вам обеспечена.

   2. Драйверы можно разделить на классы:
   - русификаторы клавиатуры  и  дисплея  (dispcccp,  keyrus,  lcgkbd,
s&tdrv и множество других);
   - драйверы мыши (msmouse, gmouse и др.);
   - драйверы управления расширенной памятью (himem, emm386, qemm);
   - драйверы  кэширования  дисков  (smartdrv,  ncache  и  др.);   они
уменьшают количество  медленных  обращений  к диску за счет хранения в
свободной памяти тех его блоков, к которым программа часто обращается;
   - драйверы, уплотняющие информацию на диске в 1.5-2 раза (stacker и
др.);
   - драйверы, шифрующие информацию на диске (diskreet и др.);
   - резидентные перехватчики вирусов, защищающие Ваши диски;
   - и еще бесчисленное множество полезных и не очень драйверов на все
возможные случаи жизни.

   3. В качестве ядра ОС используется программа MSDOS.SYS и  командный
процессор COMMAND.COM, обеспечивающий диалог с оператором.

   4. Интерфейс  с оператором типа командный язык.  Большинство команд
очень похожи на команды RT-11; есть и специфические, например:
   cd <путь> - сделать текущей указанную директорию;
   mkdir <имя> - создать новую директорию.
   Подробнее см. [4].

   5. К   собственно   системным   программам   (утилитам)   относятся
многочисленные диалоговые  оболочки  (например,   Norton   Commander),
программы обслуживания дисков (например,  Norton Utilities,  PCTools),
архиваторы (pkpak,  pkzip,  lhice,  lha,  arj),  антивирусные средства
(scan, aidstest, adinf) и другие программы, количество которых растет.

               Norton Commander (Symantec Corporation)

   Это первая программа,  которую видит любой новичок на  IBM  PC.  Ее
функции сводятся  всего  лишь  к  тому,  чтобы  избавить  оператора от
командного языка MS-DOS при помощи  меню  и  статус-строки.  Два  окна
постоянно показывают   каталог  тех  дисков,  куда  они  назначены,  и
оператору уже не приходится многократно изощряться с командой DIR  для
поиска файла,   название   которого  он  забыл.  Операция  копирования
осуществляется также предельно просто - отметить клавишей  Insert  или
мышью нужные файлы, назначить второе окно на тот каталог, куда их надо
скопировать - и скопировать.  Запустить программу совсем просто - надо
подвести к нужному файлу курсор и нажать Enter (или мышку - два раза).
   Рассказывают быль о том,  как в СССР появился Turbo Pascal 6.0  еще
до начала   его   официальной  продажи.  Якобы  представители  Borland
поставили его  на  некотором  компьютере  для  демонстрации   и,   как
нормальные цивилизованные   люди,   приняли  меры  предосторожности  -
заперли клавиатуру в сейф.  Ночью пришел один школьник, принес с собой
мышь (можно было б и клавиатуру, но она довольно большая), включил ЭВМ
и преспокойно скопировал все,  что нужно.  Правда или нет,  но  Norton
Commander вполне позволяет обойтись одной мышью.
   Опишем его основные команды, перечисленные в статус-строке:
   F1 - Help (помощь);
   F2 - Menu (настраиваемое пользователем меню);
   F3 - View (просмотр текстовых файлов);
   F4 - Edit (редактирование текстовых файлов);
   F5 - Copy (копировать);
   F6 - RenMov (переименовать - rename или переместить - move);
   F7 - MkDir (make directory - создать директорию);
   F8 - Delete (удалить);
   F9 - PullDn (войти в падающее меню Norton Commander'а);
   F10 - Quit (выйти из Norton Commander'а);

               Norton Utilities (Symantec Corporation)

   Все программы имеют простой и понятный интерфейс,  а также справку.
Приведем список некоторых наиболее употребительных утилит:
   NDD - Norton Disk Doctor - для просмотра диска на предмет дефектных
(BAD) блоков и восстановления структуры каталогов запорченного диска;
   UnErase - восстановление случайно удаленных файлов;
   SysInfo - определение характеристик и конфигурации ЭВМ;
   SpeeDisk - оптимизация диска (устранение фрагментации файлов);
   SFormat - форматирование дисков.

                          Microsoft Windows

   Microsoft Windows  3.1  запускается из среды MS-DOS,  но фактически
представляет собой полноценную ОС. Она обеспечивает:
   - универсальные   графические   процедуры  (если  на  такой-то  ЭВМ
функционирует Windows,  то  будет  работать  и  Ваша   программа   для
Windows);
   - стандартный интерфейс,  отвечающий стандарту CUA,  для самой ОС и
для всех  программ  под  Windows,  полностью  использующий возможности
графики;
   - полное  использование  ресурсов  ЭВМ  (программы под DOS не могут
стандартным образом  использовать   возможности   386   процессора   и
расширенную память);
   - многозадачность (можно одновременно  запускать  несколько  задач,
например, расчетную  на  несколько  часов,  и одновременно спокойно на
этой же ЭВМ заниматься чем-нибудь другим);
   - стандартный  обмен  данными  между  разными  программами  (можно,
например, установить связь между ними: одна будет принимать какие-либо
данные с управляемой ЭВМ аппаратуры;  электронная таблица Excel  будет
строить графики по этим данным, а Word for Windows будет автоматически
изменять эти графики в тексте подготавливаемой Вами статьи;
   - созданы     многочисленные     программы     для     "визуального
программирования" (например,  Object Vision), позволяющие построить из
стандартных кубиков нужные диалоги и отладить их,  не написав ни одной
строчки программы.
   Один маленький недостаток - когда же у  наших  заказчиков  появятся
ЭВМ, пригодные для работы с Windows?!

               Системы управления базами данных (СУБД)

   Это специальные средства для организации:
   - хранения  больших   объемов   данных   (например,   бухгалтерской
информации большого предприятия; или биржевая информация);
   - работы с этими данными;
   - обработки  различных  запросов и выдачи отчетов (как по отдельным
позициям, так и отсортированных  в  определенном  порядке  развернутых
справок).
   Системы управления  базами  данных  предоставляют  также   средства
программирования для   создания   конкретных  программ  для  конечного
пользователя - потребителя.
   От dBase,  FoxPro, Clarion для DOS до Paradox и InterBase (Borland)
для Windows.
                         Электронные таблицы

   Электронные таблицы  -  это  средство  автоматизации мелких деловых
и инженерных расчетов.  В простейшем виде -  это  плоская  таблица,  в
каждой клетке  которой  может  находиться  текст,  число  или формула.
Практически все современные  электронные  таблицы  поддерживают  обмен
информацией с  базами  данных  и  развитые  средства  деловой  графики
(трехмерные графики, диаграммы и пр.). От Super Calc и Lotus 1-2-3 для
DOS до  Microsoft  Excel  и  Quattro  Pro  (Borland)  для Windows,  от
вышеописанных деловых  расчетов  до  печати  рекламы  с  картинками  и
диаграммами и  цветных  слайд-фильмов  для презентаций (продукты фирмы
Borland - Quattro Pro и Paradox - отлично  работают  вместе,  дополняя
друг друга).
                         Издательские системы

   Продукты этого класса (Aldus  Page  Maker,  Quark  XPress,  Ventura
Publisher и  др.) являются не просто редакторами текстов,  а позволяют
полностью, в режиме WYSIWIG (What You See Is What You  Get  -  что  вы
видите, то  и  получаете),  подготовить к печати книгу любого объема и
сложности, с иллюстрациями, разными шрифтами и т.д.


                  19. TURBO PASCAL ДЛЯ IBM PC (4 ч)

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

              Системы программирования и важнейшие фирмы

   На IBM PC для одних и тех же  языков  существует  множество  систем
программирования, отличающихся   такими   параметрами,   как  удобство
работы, скорость  компиляции,  оптимизация  кода,  наличие  и   состав
библиотек вспомогательных подпрограмм. Как уже было сказано, борьбу за
первенство ведут в основном два языка  -  PASCAL  и  C++.
   Большинство системных     и     прикладных     программ    написано
профессионалами на  C.  Утверждают,  что  его  компиляторы  генерируют
компактный  оптимизированный  код.  Однако,  взяв компилятор Turbo C и
попробовав написать небольшую программу,  можно убедиться,  что размер
загрузочного  модуля  стал  не  меньше,  а  больше,  чем  у  такой  же
программы,  странслированной Turbo Pascal'ем.  Но  выводы  делать  еще
рано,  потому  что профессионалы пользуются вовсе не Turbo C,  а целым
рядом иногда менее удобных,  но  более  эффективных  трансляторов  (от
Quick C, Microsoft C до более дорогих TopSpeed C и Zortech C++).
   Что касается PASCAL'я, то конкурируют здесь в основном Turbo Pascal
(Borland Pascal)   и   TopSpeed  Pascal.  К  сожалению,  я  располагаю
подробной информацией только о продуктах фирмы Borland:
   - Borland  проводит  очень  агрессивную  политику  на мировом рынке
программных продуктов (и, в частности, в России - я, например, являясь
членом Ассоциации   групп   пользователей   Borland   -   БорАГ,  имею
возможность периодически  участвовать   в   ее   научно-информационных
семинарах, презентациях новых продуктов и конкурсах);
   - профессиональные компиляторы (фирм TopSpeed или Zortech) на нашем
рынке практически  отсутствуют  или  очень  дороги.  Отсутствие спроса
порождает также и недостаток информации.
   Итак, немного об истории фирмы Borland.  Она была основана в 1983г.
учеником Никлауса Вирта французским  математиком  Филиппом  Каном.  Он
имел всего один удачный продукт - Turbo Pascal,  основным достоинством
которого был   дружественный   интерфейс    (экранный    редактор    с
автоматическим формированием  отступов,  совмещенный  с транслятором и
простейшим отладчиком).  Эта новая  идея,  да  еще  удачное  начало  -
массовая  продажа  по  системе  почтовых  заказов  по демпинговой цене
(где-то я слышал  сумму  $5)  позволили  ему  быстро  захватить  рынок
(сегодня общее   количество   проданных   копий  пакета  Turbo  Pascal
превышает показатели для любых других компиляторов).
   1984 -  электронный  секретарь  SideKick  (80000 продаж за первые 4
месяца!).
   1985 -  отказ  фирмы от применения защиты программ от копирования и
безусловная 60-дневная гарантия.
   1987 -  Turbo  Basic  и  Turbo C,  также отличающиеся дружественным
интерфейсом интегрированной   среды;   профессиональная    электронная
таблица Quattro  (100000  продаж  за  первые  90 дней!);  приобретение
компании Ansa Software и выпуск на рынок ее разработки -  мощной  базы
данных Paradox.  Позднее  -  приобретение  корпорации Ashton-Tate с ее
СУБД dBASE и InterBase.
   1988 -  Turbo  Pascal  Professional,  Turbo  C Professional,  Turbo
Assembler&Debugger.
   Позднее - Borland C++ для DOS и Windows; ObjectVision для Windows.
   1990 - Turbo Pascal 6.0, Quattro Pro 3.0.
   1992, 1993  -  лучшие  в мире электронная таблица Quattro Pro 4.0 и
СУБД Paradox 4.0 (по оценкам  независимых  журналов)  для  DOS  и  для
Windows; Borland Pascal 7.0 для DOS и Windows.
   1993, 1994 - Borland C++ 4.0 для DOS и Windows.

   Итак, на  сегодняшний  день две крупнейшие фирмы,  уже поглотив ряд
вполне солидных фирм,  разделили сферы влияния на  рынке  программного
обеспечения для IBM PC:
   - Microsoft corporation - ОС, текстовые процессоры и др.;
   - Borland International Inc. - СУБД и электронные таблицы, массовые
компиляторы для DOS и для Windows.

                    Особенности языка Turbo Pascal

   Основной особенностью нового языка (для версий старше 5.5) является
наличие объектов.      К      сожалению,      объектно-ориентированное
программирование - слишком  объемная  тема  для  небольшого  печатного
издания. Borland Pascal для Windows также не рассматриваем,  поскольку
время его освоения даже профессионалами - месяцы.
   Перечислим некоторые  менее важные,  но полезные усовершенствования
языка (версии 6.0; 7.0 отличается незначительно):
   - большое количество базовых типов данных;
   - нетипизированные файлы (что позволяет удобно работать  с  файлами
данных произвольного формата);
   - инициализированные    переменные    (описываемые    по    прихоти
разработчиков как константы):
          const
            pi:real=3.1415926;
            a:array[1..3] of integer=(0,1,2);
            r:record x,y:integer end = (x:33; y:45);
   - нетипизированные  указатели  и   удобные   процедуры   работы   с
динамической памятью;
   - ассемблерные вставки, обработка прерываний, обработка ошибок;
   - механизм модулей как средство построения больших программ;
   - система построения оверлеев;
   - генерация различных типов кода (с использованием сопроцессора или
без, с проверкой границ массивов или без и т.п.);
   - условная компиляция участков программы;
   - возможность  приведения  типов,  снимающая  излишнюю   жестокость
стандартного PASCAL'я;
   - нетипизированные параметры подпрограмм ...
   Проиллюстрируем последние  два пункта.  Пусть надо написать функцию
для вычисления  среднего   арифметического   элементов   массива.   На
стандартном PASCAL'е это сделать невозможно (ведь в качестве параметра
в подпрограмму мы хотим передавать массив вообще - он может быть любой
длины). На Turbo Pascal'е можно сделать так:

   function Ave(var a; {нетипизированный параметр, фактически - адрес}
                Size:integer {размер массива a}
               ):real;
   type Ta=array[1..8192] of real;
   var i:integer; x:real;
   begin
     x:=0;
     for i:=1 to Size do x:=x+Ta(a)[i]; {приведение типа}
     Ave:=x/Size;
   end;

   Можно пойти еще дальше и позволить  передавать  в  функцию  массивы
разных типов:

type TTyp=(tInteger,tLongint);   {названия возможных типов}
var
  a:array[11..16] of integer;    {tInteger}
  b:array[0..9] of longint;      {tLongint}
  i: integer;

  function Sum(var a; Size:integer; Typ:TTyp):longint;
  type TI=array[1..1000] of integer; {допустимые типы параметра a}
  type TL=array[1..1000] of longint;
  var i:integer; x:longint; {здесь берем самый большой тип}
  begin
    x:=0;
    for i:=1 to Size do
      if Typ=tInteger       {анализ названия типа}
        then x:=x+TI(a)[i]
        else x:=x+TL(a)[i];
    Sum:=x;
  end;

begin
  for i:=11 to 16 do a[i]:=i; writeln(Sum(a,6, tInteger));
  for i:=0  to 9  do b[i]:=i; writeln(Sum(b,10,tLongint));
end.

                     Работа в среде Turbo Pascal

   Интегрированная среда Turbo Pascal 6.0 (или 7.0) включает в себя:
   - многооконный редактор текста;
   - компилятор и компоновщик;
   - отладчик;
   - контекстно-зависимую справочную систему.
   В соответствии со стандартом CUA среда имеет оконный  интерфейс  со
строкой меню,  рабочим столом и статус-строкой. На рабочем столе можно
располагать окна с редактируемыми текстами,  окна  просмотра  значений
переменных, трассировки вызовов процедур,  вывода для своей программы,
справочной информации.
   Удобство работы   значит  очень  много.  Получив  в  шестой  версии
многооконный редактор,  нормального  программиста  теперь   силой   не
загонишь в  пятую.  Аналогично,  после выделения цветом синтаксических
конструкций в седьмой версии, я теперь с большим трудом понимаю тексты
программ в  шестой или в других редакторах.  В седьмой версии (для 386
процессора) также добавлено ведение общего журнала объектов программы.
Если раньше я мучился, пытаясь вспомнить, в котором из 35 модулей моей
программы описана  такая-то  переменная,  то  теперь  найти  ее  очень
просто. Это помогло мне также найти одну вредную ошибку - одна и та же
константа была описана в разных модулях с разными значениями - и  одна
часть программы упорно не понимала другую.
   Переведем основные команды интегрированной среды:
   File - общие команды, файлы
     Load - загрузить файл
     Save - сохранить файл
     New  - открыть пустое окно редактора
     Exit - закончить работу
   Edit - редактирование
     Copy - скопировать выделенное на ClipBoard (разделочную доску)
     Paste - вставить сохраненный на ClipBoard фрагмент
   Compile - компиляция
     Make - скомпилировать измененные с прошлого раза файлы
     Destination (Disk/Memory) - где строить загрузочный модуль
     Primary file - файл главной программы для многомодульных программ
   Run - запуск
     Step Over - пошаговое выполнение (не заходя в процедуры)
     Trace Into - то же, но с заходом в процедуры
     Goto Cursor - выполнить программу до текущего положения курсора
   Debug - отладка
     Set Breakpoint - установить точку останова
     Evaluate -   вызвать   калькулятор  для  расчетов  или  просмотра
переменных
   Window - окно (список стандартных для любой оконной системы команд)
     Close - закрыть
     Zoom - расширить на весь экран
     Resize/Move - изменить размер/передвинуть
     Next - перейти в следующее окно
     Previous - перейти в предыдущее окно
   Options - настройка параметров интегрированной среды

                   Использование справочной системы

   Turbo Pascal для того, кто знаком хотя бы со стандартным PASCAL'ем,
не нуждается в дополнительной документации.  Все есть в нем  самом,  в
справочной системе  (разумеется,  для  этого  нужно  знать  английский
язык). Справочная  система   контекстно-зависима   (нажимая   F1,   Вы
получаете справку конкретно по текущему режиму интегрированной среды -
например, предусмотрена справка по каждому пункту меню). Текущий режим
кратко поясняется также в статус-строке.
   Справочная система  построена  в  виде  гипертекста   (многомерного
текста). Эта концепция означает следующее. Если Вы читаете словарь или
энциклопедию, в каждой статье может быть несколько ссылок на  понятия,
которые в   этом   словаре  тоже  пояснены.  Если  Вы  хотите  с  ними
познакомиться, надо долго искать их (методом последовательного поиска,
потому что  любая  книга  -  суть  одномерный,  последовательный  файл
информации). В  гипертекстной  системе  Вы  просто  указываете  нужную
ссылку -  и переходите к ней.  Всегда есть возможность также вернуться
на несколько шагов назад - и бродить по справке сколько ни угодно.
   Возможны целых четыре способа вызова справки:
   F1 - просто справка о текущем режиме;
   Alt+F1 - предыдущее окно справки;
   Shift+F1 - оглавление справки (попадаем  к  нужному  слову,  просто
набирая его по буквам);
   Ctrl-F1 - справка о слове, на котором в данный момент стоит курсор.
   Трудно придумать  что-либо  более  удобное  (разве  что  по  чтению
мысли). Но и это еще не все.  В справочной системе приведено множество
примеров по различным стандартным подпрограммам и операторам языка. Их
можно оттуда вырезать и вставлять прямо в текст своей программы:
   - подвести курсор к началу нужного фрагмента;
   - нажать Shift и стрелками выделить  фрагмент  текста,  после  чего
Shift отпустить;
   - нажать Ctrl+Insert (или через меню - Edit/Copy);
   - убрать окно справки, нажав Escape;
   - поставить курсор в нужное место в редактируемом тексте;
   - нажать Shift+Insert (или через меню - Edit/Paste);
   - нажать Ctrl+K,H для отмены выделения вставленного фрагмента.

                       Библиотеки Turbo Pascal

   Уже упоминалось,  что полезность того или иного  компилятора  не  в
последнюю очередь  зависит от количества и качества прилагаемых к нему
библиотек подпрограмм.  Turbo Pascal обеспечен  всем  необходимым  для
нормальных прикладных   программистов,  но  для  разработки  серьезных
программ (игр,  симуляторов и пр.) их,  конечно, недостаточно. Притчей
во языцех  стала медленная графика BGI (Borland Graphic Interface),  к
тому же изобилующая ошибками, практически не исправленными и в седьмой
версии. Возможно,  на них махнули рукой,  переключившись в основном на
работу под Windows (Borland Pascal). Имеются следующие библиотеки:
   - System - подпрограммы стандартных функций (Sin, Exp и т.д.);
   - Dos - подпрограммы работы с каталогами и другими тонкостями DOS;
   - Printer  -  описывает  всегда  открытый  файл  LST  для вывода на
принтер: "uses Printer; begin writeln(lst,'проверка печати'); end.";
   - Crt  -  процедуры управления текстовым режимом дисплея и вводом с
клавиатуры;
   - Graph  -  процедуры  управления  графическим  режимом  дисплея  и
рисования ряда    примитивов    (линия,    окружность,     закрашенный
многоугольник и пр.).

                    Пример интерактивной программы

   Человек гораздо  лучше  воспринимает  живой  пример,  нежели  сухой
справочник. Напишем   программу,  которая  при  максимальной  простоте
показала бы побольше интересного.
   Нарисуем закрашенный  прямоугольник  и  будем  менять  цвет и стиль
заливки  (закраски),  чтобы  посмотреть  на  возможности  BGI-графики.
Оживим  картинку,  меняя  по  нажатию  отдельных  клавиш  цвет и стиль
заливки,  и,  например,  размеры  прямоугольника  -   таким   образом,
программа будет интерактивной (взаимодействующей с оператором).
   Нам понадобятся  коды  клавиш.  Напишем  для  начала  программу для
определения кодов клавиш:

uses Crt;           {описание используемой библиотеки}
var Code: word;     {код, принимаемый с клавиатуры}
begin
  repeat
    writeln(ord(ReadKey));  {прием и распечатка кода клавиши}
  until false;
end.

   Эта программа  со  своим условием окончания цикла будет выполняться
бесконечно, но это не страшно - находясь в интегрированной  среде,  мы
всегда сможем    остановить   эту   программу   нажатием   Ctrl-Break.
Поэкспериментировав с ней, убедимся:
   - функция  ReadKey  ожидает  нажатия клавиши (программа в это время
стоит);
   - некоторые  клавиши  (стрелки)  возвращают  два  кода подряд - 0 и
информационный; одинаковый для всех  0  можно  не  учитывать  в  нашей
простой программе;
   - запишем коды клавиш,  которые могут понадобиться  (пусть  стрелки
меняют размер  прямоугольника,  Enter  - стиль и Space (пробел) - цвет
заливки.
   Теперь напишем  саму  программу  (можно  в этом же окне,  а можно и
второе открыть):

{
  Демонстрация цвета и стиля заливки
  с изменением цвета, стиля заливки и размеров прямоугольника
  с клавиатуры

  Alek Soft (C) 1993
}
uses Graph, Crt; {описание используемых библиотек}
const
  Space  = 32;   {коды используемых клавиш}
  Enter  = 13;
  Up     = 72;
  Down   = 80;
  Left   = 75;
  Right  = 77;
  Escape = 27;
var
  Gd,Gm: integer; {переменные для иницаиализации графики}
  Code:  word;    {код, принимаемый с клавиатуры}
  X,Y:   integer; {координаты левого верхнего угла прямоугольника}
  Dx,Dy: integer; {размеры прямоугольника}
  Color,Style: word; {цвет и стиль заливки}

  {
    Процедура изменяет размеры прямоугольника на (dDx,dDy),
    проверяя, не выходит ли он за границы экрана
  }
  procedure Grow(dDx,dDy: integer);
  begin
    dDx:=Dx+dDx; dDy:=Dy+dDy;  {новые размеры}
    if (X+dDx>=0) and (X+dDx<=GetMaxX) and
       (Y+dDy>=0) and (Y+dDy<=GetMaxY) then begin
      Dx:=dDx; Dy:=dDy;
    end;
  end;

begin
  Gd:=Detect;  {BGI сам определит тип дисплея}
  InitGraph(Gd,Gm,'');             {инициализировать графику}
  if GraphResult<>grOk then halt;  {закончить, если не удалось}
  {задать начальные значения координатам и размерам прямоугольника}
  X:=GetMaxX div 4; Dx:=GetMaxX div 2;
  Y:=GetMaxY div 4; Dy:=GetMaxY div 2;
  {начальные значения цвета и стиля заливки}
  Color:=Blue;  Style:=SolidFill;
  repeat
    ClearDevice;     {очистить экран}
    SetFillStyle(Style,Color);  {установить стиль и цвет заливки}
    Bar(X,Y,X+Dx,Y+Dy);         {нарисовать закрашенный прямоугольник}
    repeat
      Code:=ord(ReadKey);       {принять код нажатой клавиши}
      case Code of
        Up:    Grow(0,-16);               {изменение размеров}
        Down:  Grow(0, 16);
        Left:  Grow(-24,0);
        Right: Grow( 24,0);
        Enter: if Style<CloseDotFill      {листание стиля заливки}
                  then inc(Style)
                  else Style:=SolidFill;
        Space: if Color<GetMaxColor       {листание цвета заливки}
                  then inc(Color)
                  else Color:=Black;
      end;
      {повторять цикл, пока не будет дана команда, изменяющая рисунок}
    until Code in [Space,Enter,Up,Down,Left,Right,Escape];
  until Code=Escape;
end.

   Ужас! Здесь  в  каждом  операторе  -   незнакомые   слова.   Ничего
страшного. Половина  из  этих  слов  - стандартные процедуры из модуля
Graph, другая - константы,  описанные в том  же  модуле.  Подведите  к
каждому из них курсор и нажмите Ctrl-F1 - увидите,  что это,  откуда и
для чего (если знаете английский язык).
   Почти наверняка  Вы  ничего  не получите,  запустив эту программу в
первый раз.  Дело в том,  что процедура InitGraph ищет в указанном  ей
каталоге (в данном случае - в текущем) BGI-драйвер для соответствующе-
го типа дисплея. Если Вы работаете с EGA или VGA-монитором, скопируйте
из каталога TP\BGI драйвер EGAVGA.BGI в свой каталог.  Если Ваша ЭВМ -
"Поиск", то берите CGA.BGI (мне Вас искренне жаль).
   Обратите внимание:
   - наверняка многие из  Вас  вспоминают  коды  и  номера  цветов  из
BASIC'а школьной  ЭВМ.  Здесь  номера  от  Вас скрыты именами констант
(Red, Green,  Blue) - так гораздо удобнее.  Это не свойство  языка,  а
всего лишь библиотека подпрограмм!
   - с точки зрения графики программа написана предельно универсально:
BGI-драйвер сам  определяет  тип  дисплея;  функции  GetMaxX и GetMaxY
возвращают максимальные координаты для данного графического режима,  а
программа все размеры привязывает к ним.  Будучи запущенной на том или
ином дисплее, начальный размер прямоугольника будет в половину экрана.
То же касается верхнего предела диапазона цветов - GetMaxColor;
   - проще было бы написать программу,  работая не с размером  прямоу-
гольника (Dx,Dy), а с координатами его правого нижнего угла. Но именно
так сделано для удобства дальнейшего развития программы.  Вы легко мо-
жете вместо   Bar(X,Y,X+Dx,Y+Dy)  поставить  FillEllipse(X,Y,Dx,Dy)  и
посмотреть,  как рисуется эта и  другие  фигуры.  Наконец,  Вы  можете
переделать  процедуру Grow,  чтобы она меняла не размеры,  а начальные
координаты (X,Y) - и получите перемещаемую стрелками фигуру.

   Помните, самое главное в нашем  предмете  -  практика!  А  практика
быстро приходит, если есть интерес. Все остальное - будет!


                              ЛИТЕРАТУРА

   1. Йенсен  К.,  Вирт  Н.  Паскаль:  руководство  для пользователя и
описание языка. - М.:Финансы и статистика, 1982, 1989.
   2. Перминов С.Н. Язык программирования Паскаль. - М.:Радио и связь,
1988.
   3. Новичков В.С.  и др.  Паскаль:  Учеб.пособие для сред.спец.учеб.
заведений - М.:Высш.шк., 1990. - 223с.
   4. Абрамов С.А., Зима Е.В. Начала информатики. - М.:Наука, 1989.
   5. IBM PC для пользователя. - М.:Финансы и статистика, 1990, 1991.
   6. Руководство  по  системе  Turbo  Pascal 6.0.  - М.:МП "Системы и
комплексы", 1992. - 270 с.

                  Задачи для самостоятельной работы

   7. С.А.Абрамов,  Г.Г.Гнездилова и др. Задачи по программированию. -
М.:Наука, 1988.
                            Факультативно:

   8. Система коллективного пользования "Фокус": методические указания
к лабораторным  работам  в  дисплейных  классах  ЕС  ЭВМ  в  ОС  ЕС  /
Чуваш.ун-т; сост. В.М.Пупин. Чебоксары, 1988.
   9. Электротехнические  расчеты  на  ЭВМ  в диалоговом режиме (пакет
прикладных программ в Бейсик-системе):  методические  указания/  сост.
В.М.Шевцов,  Л.В.Шабунин,  С.А.Лазарев,  С.И.Глинчевский; Чуваш. ун-т,
Чебоксары, 1988.
   10. Митрюхин  В.К.,  Донской  А.Н.,  Михайлов   А.В.,   Немов  А.М.
Программирование на  БК-0010-01.  -  РИО  Чебоксарской типографии N 1,
Чебоксары, 1993.
   11. Архитектура среды для разработки приложений. - Киев:"Крещатик",
1992.
   12. А.Федоров,  Д.Рогаткин. Borland Pascal в среде Windows. - Киев:
"Диалектика", 1993. - 656 с.


   УЧЕБНОЕ  И  ПРИКЛАДНОЕ  ПРОГРАММНОЕ  ОБЕСПЕЧЕНИЕ  НА  ЭВМ  СМ-4

   1. А.Н.Донской.  Автоматизированный обучающий курс "ОЗНАКОМЛЕНИЕ  С
КЛАВИАТУРОЙ".
   2. А.Н.Донской.    Автоматизированный    обучающий    курс    "ЯЗЫК
ПРОГРАММИРОВАНИЯ  PASCAL":
 - ТИПЫ  ДАННЫХ  В  ЯЗЫКЕ  PASCAL.   ПРАВИЛА   ЗАПИСИ   АРИФМЕТИЧЕСКИХ
ВЫРАЖЕНИЙ.
 - ВВОД/ВЫВОД В ЯЗЫКЕ PASCAL.
   3. А.Н.Донской.  Автоматизированный  обучающий  курс  "ОПЕРАЦИОННЫЕ
СИСТЕМЫ. ВВЕДЕНИЕ В ОС NTS".
   4. Библиотека программ для научных и инженерных расчетов BIR / каф.
САУЭП, каф. ТОЭ ЧувГУ.
   5. А.Н.Донской.   Библиотека   подпрограмм   ALLIB   для  работы  с
клавиатурой, дисплеем и графопостроителем.

   Факультативно:

   1. Автоматизированный  обучающий  курс  "BASIC"  / ИвЭИ,  каф.САУЭП
ЧувГУ.
   2. А.Н.Донской.  Автоматизированный  обучающий курс "Порядок работы
на языке FORTRAN в ОС NTS".
   3. Библиотека   подпрограмм   для    научно-технических    расчетов
БНТР-РАФОС.
   4. Библиотека  подпрограмм  для научно-технических расчетов SSP для
OS ЕС ЭВМ.
Home Page

http://simulators.narod.ru
Post: 428000, г.Чебоксары, а/я 121,
Донской Алексей Николаевич
Адрес изображен картинкой для защиты от спам-роботов
Rambler's Top100
Сайт управляется системой uCoz