Терехин В.В.
TURBO PASCAL Учебное пособие
Новокузнецк 2004
Министерство Образования Российской Федерации Кемеровский...
96 downloads
557 Views
813KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Терехин В.В.
TURBO PASCAL Учебное пособие
Новокузнецк 2004
Министерство Образования Российской Федерации Кемеровский государственный университет В. В. Терёхин
TURBO PASCAL Учебное пособие
Новокузнецк 2004 ББК 32.973 2
УДК - 681.142.2 ТПечатается по решению редакционно-издательского совета Кемеровского государственного университета. Рецензенты: ………………… ……………….. Терёхин В.В. TURBO PASCAL:Учебное пособие /Кемеровский государственный университет. – Новокузнецк: Кузбассвузиздат, 2004. -376с. ISBN …………………… Автор скомпоновал основные сведения
Книга рассчитана на студентов 2-5 курсов по специальности «Прикладная математика и информатика», «Автоматизированные системы обработки информации и управления», «Прикладная информатика в экономике». К ……………..
ББК 32.81
ISBN …………………… © Терёхин В.В., 2004 © Кем еровский государственный университет, 2004
3
Содержание 1 Введение. Интегрированная среда Турбо Паскаля............................6 2 Aлфавит Турбо Паскаля ..........................................................................12 3 Идентификаторы .......................................................................................13 4 Структура программы .............................................................................13 5 Описание переменных ..............................................................................15 6 Описание констант....................................................................................16 7 Описание и использование меток ..........................................................17 8 Классификация типов данных. Стандартные типы данных ............18 8.1 Классификация типов данных.........................................................18 8.2 Целочисленные типы.........................................................................19 8.2 Логический тип...................................................................................20 8.3 Символьный тип ................................................................................20 8.4 Вещественные тип..............................................................................21 9 Описание типов пользователя ................................................................22 10 Выражения...............................................................................................24 10.1 Порядок выполнения операций.....................................................24 10.2 Выражения целого типа ..................................................................25 10.3 Вещественные выражения..............................................................26 10.4 Логические выражения ...................................................................27 11 Операторы ввода/вывода.......................................................................31 11.1 Операторы ввода (Read, Readln)....................................................32 11.2 Операторы вывода (Write, Writeln) ..............................................32 12 Оператор присваивания.........................................................................35 13 Составной оператор ................................................................................36 14 Использование стандартных процедур и функций...........................37 14.1 Понятие формальных и фактических параметров .....37 14.2 Некоторые полезные процедуры и функции...............................39 15 Условный оператор (ветвление)...........................................................42 16 Оператор множественного выбора (варианта) - case........................44 17 Циклы........................................................................................................47 17.1 Цикл типа for ....................................................................................47 17.2 Цикл типа While ...............................................................................53 17.3 Цикл типа Repeat... Until .................................................................55 17.4 Дополнительные операторы при программировании циклов ....................................................................57 18 Массивы ....................................................................................................58 18.1 Одномерные массивы ......................................................................59 18.2 Сортировка одномерного массива ................................................62 18.3 Массивы с большей размерностью ...............................................64 18.4 Констант-массивы............................................................................69 4
19 Строки .......................................................................................................72 19.1 Строковый тип .................................................................................72 19.2 Операции над строками ..................................................................74 20 Генератор случайных чисел ..................................................................82 21 Записи ........................................................................................................84 21.1 Записи с фиксированными частями .............................................84 21.2 Записи с вариантами........................................................................88 22 Множества ................................................................................................91 23 Процедуры и функции описанные пользователем .................93 24 Параметры-переменные не имеющие типа........................................98 25 Процедурные типы ................................................................................99 26 Файлы......................................................................................................103 26.1 Типизированные файлы ...............................................................104 26.2 Текстовые файлы ...........................................................................118
5
1 Введение. Интегрированная среда Турбо Паскаля Одним из самых популярных я распространенных программных продуктов фирмы Borland по праву считается интегрированный пакет Turbo Pascal. Программная среда Турбо Паскаля называется интегрированной потому что состоит из редактора, компилятора, компоновщика, отладчика и обеспечивает создание, отладку и исполнение программ, написанных на Паскале, без обращения к каким-либо другим программным средствам. При запуске среды Турбо Паскаля появляется три видимых управляющих элемента: полоса меню в верхней части, область окна в центре и строка статуса внизу. Активизация верхней полосы меню – F10, перемещение по пунктам меню - клавиши: вверх, вниз, вправо, влево, а для выбора подменю - клавиша Enter. Существует возможность использования «горячих клавиш» нажимаются одновременно клавиша Alt и подсвеченная буква заголовка меню. Выход из меню - клавиша Esc. Необходимо отметить, что некоторые пункты меню являются недоступными в том случае, когда нет смысла их выбирать. Практически вся работа в среде Турбо Паскаля происходит в окнах. Окно - это прямоугольная область экрана, которую можно перемещать, изменять ее размеры, перекрывать, закрывать и открывать. Активное окно — это окно, с которым Вы в настоящий момент времени работаете. Существуют несколько типов окон, но большинство из них имеют общие элементы: - полоса заголовка; - закрывающая кнопка; - полосы скроллинга; - уголок для изменения размеров окна; - кнопка масштабирования; - номер окна. Легко отличить активное окно от неактивного по двой6
ной рамочке. Активное окно всегда имеет закрывающую кнопку, кнопку масштабирования, кнопки перемещения и уголок изменения размеров. Если окна перекрываются, то активное окно всегда находится на переднем плане. Как мы уже говорили, каждое открытое окно имеет уникальный номер в верхнем правом углу. При нажатии Alt-О можно получить список всех открытых окон. Для того, чтобы сделать окно активным, можно воспользоваться комбинацией клавиш Alt<номер окна>. Строка статуса появляется в самой нижней части экрана и выполняет следующие функции: • напоминает основные комбинации клавиш для быстрого доступа к командам меню; • предоставляет быстрый вариант выполнения команд посредствам выбора активных зон мышью; • информирует о работе среды (например, сообщение "Saving... " появляется, когда сохраняется редактируемый файл); • предоставляет краткую справочную информацию по командам меню. Следующим важным средством управления средой являются диалоговые окна. Диалоговое окно - это наиболее удобный способ показать и сделать многочисленные установки среды. Диалоговое окно может содержать пять основных элементов: зависимые переключатели, независимые переключатели, кнопки, окна ввода и окна списка. Для перехода от одного элемента диалогового окна к другому (в порядке установленном создателями среды) можно использовать клавишу Tab или комбинацию Shift—Tab. Эти возможности отличаются только направлением обхода. Каждый элемент, когда он становится активным, подсвечивается. Редактор Турбо Паскаль имеет следующие возможности: • редактирование нескольких больших файлов (до 1 Мб); • открытие нескольких файлов одновременно; 7
• передвижение, перекрытие и изменение размеров окон редактора; • копирование текста из окна справочной системы; • копирование между окнами. Для выделения части текста при копировании, удалении и т. д. можно воспользоваться комбинацией клавиш Shift и стрелок. Занести фрагмент в буфер обмена - Ctrl-Insert, а из буфера обмена - Shift-Insert, удалить - Ctrl-Delete, перенести в буфер обмена - Shift-Delete. Одним из основных достоинств среды является встроенная контекстно-ориентированная справочная система, которая позволяет получить справочную информацию посредством нажатия клавиши F1. Меню Help (Alt-H) позволяет использовать расширенные возможности системы: поиск в оглавлении (Shift-F1), контекстный поиск (Ctrl-F1), просмотр предыдущей страницы помощи (Alt-Fl). Если вы уже находитесь в справочной системе можно получить информацию о самой системе (F1). Любой справочный экран может содержать одно или более ключевых слов (подсвеченных элементов), дающих возможность получения дополнительной справочной информации. Команды работы с файловой системой находятся в верхним меню File. Сохранить файл из активного окна на диск - Save (F2), загрузить с диска — Open (F3), сохранить содержание активного окна под новым именем - Save as..., сохранить содержание всех окон - Save all и т. д. В меню Edit продублированы основные команды работы с блоками и команда последовательной отмены действий в редакторе -Undo (Alt-BkSp). Меню Run предназначено для запуска программы в обычном режиме - Run (Ctrl-F9), в режимах пошаговой отладки и т. д. В меню Compile собраны команды связанные с компиляцией программ: Compile (Alt-F9), Make (F9) и др. Меню Debug позволяет управлять отладкой программы, получать и устанавливать отладочную информацию: устанавли8
вать точки прерывания программы - Breakpoints, наблюдать за значениями переменных, вызывая специальное окно - Watch, смотреть за окном, в котором выполняется программа - User screen (Alt-F5), и т. д. Меню Tools позволяет осуществлять управление инструментарием среды, в том числе и вызывать внешние программы— утилиты: турбо-ассемблер, турбо-дебагер и т. д. В меню Options собраны команды конфигурирования и настройки среды Турбо Паскаль. Меню Window содержит команды управления окнами: порядок расположения окон на рабочей поверхности — Tile, Cascade, закрытия: Close и Close all, перехода: Next (F6), Previous (ShiftF6) и др. Встроенный отладчик среды Турбо Паскаля позволяет производить пошаговое выполнение программы и просматривать переменные: каким образом изменяются их значения во время выполнения операторов программы. Чтобы начать сеанс отладки, необходимо выбирать команду Run/Trace Into или нажать F7. Если программа изменялась с момента последней компиляции, то она будет откомпилирована снова. После этого на служебном слове begin в начале программы появится подсвеченная полоса, которая будет переходить от строки к строке по мере нажатия клавиши F7 и выполнения очередного оператора программы. Для вызова окна наблюдения за значениями переменных можно воспользоваться командой Debug/Add Watch (или Ctrl-F7). При выборе данной команды появляется строка редактирования, в которой требуется указать имя переменной для наблюдения. Для удобства работы неплохо изменить размер и положение окна для редактирования программы и окна Watch, избежав перекрытий. После этого при пошаговой отладке можно видеть значения переменных на каждом шаге. Иногда необходимо начать отладку программы не с первой строки. В этом случае можно воспользоваться точками прерывания (Debug\Breakpoints), тогда при обычном запуске про9
грамма будет выполняться не до конца, а до строки, в которой эта точка установлена. В таблицах 1-3 приведены «горячие» клавиши различных режимов. Таблица 1- «Горячие» клавиши редактирования Клавиша(и) Элемент меню Функция Удаляет выделенный текст из окCtrl-Del Edit/Clear на и не помещает его в буфер обмена. Копирует выделенный текст в Ctrl-Ins Edit/Copy буфер обмена. Помещает выделенный текст в Shift-Del Edit/Cut буфер обмена и удаляет его. Помещает текст из буфера обмена Shift-Ins Edit/Paste в активное окно. Ctrl-L Search/ Search Повторяет команду Find или Again Replace . Сохраняет файл в активном окне F2 File/Save редактора. Позволяет открыть файл. F3 File/Open Таблица 2 – «Горячие» клавиши управления окнами Клавиша(н) Элемент меню Функция Alt-# Alt-O Alt-F3 Alt-F5 Shift-F6
Показывает окно, где # - номер окна, которое Вы хотите посмотреть. Показывает список открытых Window/List окон. Закрывает активное окно. Window/Close Показывает экран пользоватеWindow/User ля. Screen Window/Previous Проходит назад через все открытые окна. нет
10
F5 F6 Ctrl-F5 Shift-F5
Увеличивает (уменьшает) активное окно. Проходит вперед через все Window/Next активные окна. Window/Size/Move Изменяет позицию активного окна (сдвиг - стрелками). Window/Size/Move Изменяет размер активного окна (изменение – Shift + стрелка). Window/Zoom
Таблица 3 - «Горячие» клавиши отладки/запуска Клавиша(и) Элемент меню Функция Alt-F9
Compile/Compile
Компилирует последний файл в редакторе.
Ctrl-F2
Run/Program Reset
Переустанавливает выполняемую программу.
Ctrl-F4
Debug/Evaluate/ Вычисляет выражение. Modify Debug/Add Watch Добавляет выражение для просмотра.
Ctrl-Fl
Ctrl-F9 F4
Debug/Toggle Breakpoint Run/Run Run/Go To Cursor
Устанавливает или очищает условные точки прерывания. Запускает программу. Запускает программу до позиции курсора.
F7
Run/Trace Into
Выполняет прослеживание внутри процедур.
F8
Run/Step Over
Осуществляет перескакивание через вызовы процедур.
Ctrl-F7
11
F9
Compile/Make
Выполняет Make (компилирует/редактирует связи) программы.
2 Aлфавит Турбо Паскаля В алфавите Турбо Паскаля используются следующие символы: • буквы латинского алфавита как в верхнем A.. Z, так и в нижнем a.. z регистрах; • десятичные цифры 0.. 9; • шестнадцатеричные цифры 0.. 9, А, В, С, D, E, F; • прописные и заглавные буквы русского алфавита А... Я, а... я и символы псевдографики; • специальные символы + , _ , * , / , = , > , < , [ , ] , ( , ) ; • символы знаков препинания (. , : ;) ; • пары специальных символов <=, | >= , := , .. , (* , *) . В таблице 4 приведены 48 наиболее употребительных ключевых слов языка Турбо Паскаль. Всего же их около восьмидесяти. Зарезервированные (ключевые) слова могут употребляться только по своему прямому назначению. Таблица 4 – Ключевые слова языка Паскаль. absolute end inline procedure and external interface program array file interrupt record begin for label repeat case forward mod set const function nil shl div goto not shr do if of string downto implementaor then else in packed to
type unit until uses var while with xor
12
3 Идентификаторы Идентификатор представляет собой последовательность из латинских букв, цифр и символа подчеркивания, которая всегда начинается с буквы или символа подчеркивания. Длина идентификатора ограничивается только длиной строки, но значимыми являются только первые 63 символа. Заглавные и прописные буквы в идентификаторах не различаются. Идентификаторы используются как имена программ, типов, констант, переменных, меток, процедур и функций. Недопустимо использование ключевых слов в качестве идентификаторов. Примеры: А, С, Top_Not, RLeft_m - допустимые идентификаторы; 2Sum - недопустимый идентификатор, т. к, начинается с цифры; Now Elem - недопустимый идентификатор, т. к. содержит пробел; Sin - недопустимый идентификатор, так как совпадает с именем стандартной тригонометрической функции. 4 Структура программы Основная часть программы написанной на Паскале описательные разделы. Поэтому, говоря о структуре программы следует отметить следующее. 1. Некоторые описательные разделы могут отсутствовать в реальной программе, а их порядок и повторное включение зависят только от внутренней логики программы. Например, если в описании типа используется константа, то описание этой константы должно предшествовать описанию типа и т. д., а если при описании следующей константы потребуется данный тип, то необходимо открыть раздел описания констант второй раз и т. д. 2. Структуру программы в точности повторяют структуры подпрограмм: процедуры и функции. Они отличается 13
только заголовком и заключительным символом: тело программы заканчивается служебным словом END с точкой (end.), а тело подпрограммы - с точкой-запятой (end;). Структура программы: program <Идентификатор — имя программы>; const <раздед описания констант>; type <раздел описания типов>; var <раздел описания переменных> ; label <раздел описания меток>; <раздел описания процедур и функций>; begin <тело программы — операторный блок> ; end. Самая простая программа может не содержать оператора program, описательных разделов и иметь пустое тело. Однако, она может быть откомпилирована и запущена. begin end. Следующая программа будет посложней, она выводит на экран сообщение: program p2; be g in writeln('Моя первая программа на Паскале'); end. Программа, которая получает два числа и выводит их сумму на экран, содержит один описательный раздел - раздел описания переменных: 14
program рЗ; var А, В: integer; begin Writeln('Введите два числа А и В’); Read(А, В); Writeln(‘A + В= ' , А + В) ; end. Ясно, что программы могут быть очень сложными, содержать все разделы описания, процедуры и функции. По мере знакомства с языком мы их также будем использовать. 5 Описание переменных Каждая переменная, встречающаяся в программе, должна быть описана. Описание переменной должно предшествовать ее использованию. Раздел описания переменных может повторяться в программе несколько раз. Он начинается служебным словом Var, a заканчивается с началом любого другого описательного раздела, заголовком процедуры, функции или началом тела программы. Раздел описания переменных может быть вложен в описание процедуры или функции. При этом переменные, описанные в основной программе, называются глобальными, а в подпрограммах - локальными. При компиляции под каждую переменную выделяется участок памяти, размер которого зависит от типа переменной. Сама переменная определяется, во-первых, именем - идентификатором, во-вторых - типом, а в-третьих - значением, которое записывается в выделенный участок памяти. В дальнейшем вы поймете, что изменить значение переменной можно путем изменения соответствующего участка памяти, но до тех пор мы будем считать, что для этого необходимо использовать оператор присваивания. 15
Итак, структура описания переменных такова: var <список идентификаторов 1>: <тип переменных>; <список идентификаторов 2>: <тип переменных>; …………………………………………………………. <список идентификаторов n>: <тип переменных>; Примеры: 1) var А, В, С, D: Real; I, J, К, L, M: Integer; U: Char; S: String; 2) var M: array [1.. 100] of Integer; T: Real; N, I: Integer; 6 Описание констант Существует два вида констант: константы "без типа" - нетипизированные и константы "с типом" типизированные (типированные). Их различия обусловлены процессом компиляции программ (компиляция - преобразование программы с алгоритмического языка в машинный код). При написании программ для нас имеет значение то, что типизированные константы изменяемы во время выполнения программы, а нетипизированные изменены быть не могут. В свою очередь нетипизированные константы могут быть использованы при описании других констант, переменных и типов. Структура описания нетипизированных констант: 16
<идентификатор - имя константы>= <значение>; Примеры: const А = 75; Т = 3. 56; S = 'ABCD'; Структура описания типизированных констант: <идентификатор—имя константы> : <тип константы> = <значение>; Примеры: const A: Integer 43; R: Real= 345. 123; S: String[10]= 'Мама мыла раму'; 7 Описание и использование меток Использование меток при программировании на структурированных, процедурных языках (к которым относится Паскаль) считается дурным тоном. Но бывают случаи, когда для использования меток существуют веские причины, например, начальный этап создания программы, когда автор еще не видит другого решения задачи. Идентификатор метки может начинаться с цифры. Описание меток: Label <список идентификаторов - имен меток, через запятую>; Метки ставятся в том блоке программы, в котором они описываются. Метку можно поставить перед любым оператором, 17
что позволяет выполнить прямой переход на этот оператор. Метка в программе: <идентификатор - имя метки>: Переход к метке осуществляется следующим образом (оператор перехода к метке): Goto <Идентификатор — имя метки>; Пример: program p4; label 11, 12, 13, 14; begin goto 11; 13: Writeln('метка 3 ' ) ; goto 14; 12: Writeln('метка 2 ' ) ; goto 13; 11: Writeln{'метка 1 ' ) ; goto 12; 14: end. Программа выводит на экран сообщения по порядку: ‘метка 1’ , 'метка 2’ , 'метка 3’, но операторы вывода в программе расположены в обратном порядке. Такой эффект достигается использованием меток. 8 Классификация типов данных. Стандартные типы данных 8.1 Классификация типов данных При объявлении переменной необходимо указать ее тип. Тип переменной описывает набор значений, которые она может принимать, действия, которые могут быть над ней выполнены, а также указывает на объём оперативной памяти, занимаемой переменной данного типа. Объявление типа опреде18
ляет идентификатор, который собственно и обозначает тип. Имеется шесть основных классов типов: • простые типы; • строковые типы; • структурные типы; • тип указатель; • процедурные типы; • объектные типы. Простые (скалярные) типы разделяются на два блока: • порядковые; • вещественные. Turbo Pascal имеет семь встроенных порядковых типов: Integer (целый), Shortint (короткий целый), Longint (длинный целый), Byte (длиной в байт), Word (длиной в слово), Boolean (логический) и Char (символьный). Кроме того, имеется два других класса порядковых типов, определяемых пользователем: перечислимые типы и типы поддиапазона. 8.2 Целочисленные типы Первые пять порядковых типов относятся к категории целочисленных типов. В графе “Тип” в нижележащей таблицы приведены ключевые слова - обозначения целых типов. Каждый из них определяет некоторое подмножество целых чисел. Тип Shortint
Диапазон -128.. 127
Размер в памяти 1 байт
Integer
-32768. .32767
2 байта
Longint
-21212147483648..2147483647
4 байта
Byte
0..255
1байт
Word
0..65535
2 байта 19
8.2 Логический тип Значениями логического типа являются встроенные идентификаторы False и True. Этот тип переменных задаётся ключевым словом Boolean. Поскольку логический тип является перечислимым, между этими значениями имеют место следующие отношения: False < True .
8.3 Символьный тип Множеством значений этого типа являются символы, упорядоченные в соответствии с расширенным набором символов кода ASCII. Тип этих переменных задаётся ключевым словом Char. Значение переменной символьного типа можно задать в операторе присваивания с помощью символьной константы или функции Chr . Эта функция устанавливает соответствие между однобайтовыми целыми значениями кода и символами. Противоположной по отношению к Chr является функция Ord, которая возвращает код символьного аргумента. Знак # и последующая за ним целая беззнаковая константа обозначают символ. Например, в программе есть описание Var ch: Char; Тогда операторы ch: = chr(7); ch: = #7; присваивают символьной переменной ch одно и то же символьное значение – звуковой сигнал (это управляющий символ). В работе могут понадобится значения некоторых управляющих символов, которые приведены ниже. Код Ctrl - поИспользоМнемоДействие 20
следовательность #7
^G
вание функции Chr Chr(7)
ническое обозначение BEL
#10
^J
Chr(10)
LF
#12
Chr(12)
FF
#13
Chr(7)
CR
#26 #27
Chr(7) Chr(7)
SUB ESC
Звуковой сигнал динамика Перевод строки Прогон страницы Возврат каретки Конец файла Символ Escape
8.4 Вещественные тип В Турбо Паскале имеются пять встроенных вещественных типов: Real, Single, Double, Extended и Сотр. Вещественные типы различаются диапазоном и точностью значений. Тип Диапазон Значащие цифры Размер в (кол-во) байтах Real
2.9Б-39..1.7Е38
11-12
6
Single
1.5E-45..3.4E38
7-8
4
Double
5.0E-324..1.7E308
15-16
8
Extended
3.4E-932..1.1E4932
19-20
10
Comp
-9.2el8.. 9.2el8
19-20
8
Turbo Pascal поддерживает две модели генерации кода для выполнения действий над вещественными типами (для чисел с плавающей точкой): программную и аппаратную. Выбор соответствующей модели осуществляется с помощью директи21
вы компилятора SN. При отсутствии математического сопроцессора директива компилятора в тексте программы $Е обеспечит его полную эмуляцию. 9 Описание типов пользователя Существует несколько возможностей создания (описания) типов пользователя. 1. Полное соответствие стандартному типу. В этом случае переменные нового типа обладают такими же свойствами, что и переменные базового типа. type <идентификатор — имя типа>= <идентификатор стандартного базового типа>: Примеры: type Mylnteger = Integer; MyReal = Real; MyString = String[255]; 2. Поддиапазон стандартного типа. Такие типы называются ограниченными. Базовым типом в этом случае может быть любой перечислимый тип. Ограниченный тип сохраняет все свойства базового и отличается лишь тем, что имеет ограниченный диапазон значений. Использование ограниченных типов, определенных пользователем, значительно улучшает наглядность и читаемость программ. Кроме того, специальная опция компилятора позволяет включить контроль выхода переменных за пределы объявленного диапазона, а это удобно при отладке программы. Включение контроля — {$R+}, а выключение его — {$R—}. type < идентификатор - имя типа > = < начальное значение бaзового типа >.. < конечное значение базового типа >; 22
Примеры: 1) type Mylnteger = -327.. 327; СharS = 'A ' .. 'Z ' ; 2) program p5 ; type MyType = 0. . 9; var varl, var2 : MyType; begin {$R-} varl: =5 5; {выход за диапазон, но ошибки не возникает} {$R+} var2:= 55; {выход за диапазон, ошибка} end. 3. Новый перечислимый тип. При его определении в соответствующем порядке задаются все необходимые значения, которые может принимать переменная данного типа. Значения переменных нового типа будут представлены при помощи идентификаторов, которые, таким образом, будут являться константами нового типа. Примеры: type Operator = ( plus, minus, multi, divide ); Day = ( Mon, Tues, Wed, Thur, Fri, Sat, Sun); Из примера видно, что переменные типа Operator могут принимать одно из четырех значений plus, minus, multi, divide. Операции отношения =, <>, >, <, >=, <= можно применять ко всем переменным перечислимых типов, в том числе к 23
описанным пользователем. Для определения результата операций сравнения мы записываем значения в нужном порядке. Для типа Operator в приведенном выше примере справедливы соотношения: plus < minus < multi < divide. 10 Выражения 10.1 Порядок выполнения операций Запись, содержащая константы, переменные, обращения к функциям и знаки операций, называется выражением. При этом константы, переменные и обращения к функциям обозначают общим словом операнд. В зависимости от типа операндов, от используемых операций и функций значения выражений различаются по типам. Для нас важно выделить целые, вещественные, логические, символьные и строковые выражения, которые приходится чаще всего использовать в программах. Порядок выполнения операций определяется их старшинством, или приоритетом. Ниже в таблице приведены приоритеты операций. Первый уровень приоритетов является наивысшим, а четвёртый – низшим. Операция not *, /, div, mod, and, shl, shr +, -, or, xor =, <>, <, >, <=, >=, in
Приоритет Первый Второй Третий Четвёртый
При определении порядка выполнения операций следует учитывать следующее: • операнд, находящийся между двумя операциями с различными приоритетами, относится к операции, имеющей более высокий приоритет; • операнд, находящийся между двумя операциями с равны24
• • •
ми приоритетами, относится к той операции, которая находится слева от него; выражение, заключенное в скобки, перед использованием вычисляется как отдельный операнд; любая операция внутри скобок производится раньше операции за скобками; операции с равным приоритетом выполняются слева направо, если этот порядок не изменён с помощью круглых скобок. 10.2 Выражения целого типа В целых выражениях допускаются следующие операции: + - сложение; - вычитание; * - умножение; div - целочисленное деление; mod - остаток от деления величины a на b: a mod b = a-((a div b) * b). Примеры (использование операций mod и div): t : = 5 div 2; {t = 2} z: = 5 mod 2; {z = 1}
В выражениях целого типа операнды могут быть только целого типа: целые значения, целые константы и переменные, функции, имеющие целые значения и т. д. Для указания порядка выполнения операций в выражениях допускается использование круглых скобок. Примеры (выражения): (а + b) * с - m div 3 c * 2 +k * k - 3 * a ((а + b) mod (d * f)) * 12 При составлении выражений следует руководствоваться выше указанными правилами. Однако, если вы сомневаетесь в по25
рядке выполнении операций, то используйте скобки. Трудно ошибиться, используя лишние скобки, но отсутствие скобок очень часто приводит к ошибкам. Приведём тип результата четырех стандартных функций: function Abs( x: ): ; — функция возвращает абсолютное значение (модуль) параметра х. Значение функции совпадает по типу со значением параметра. function Sqr(x: ): ; функция возвращает квадрат значения параметра х. Значение функции совпадает по типу со значением параметра. function Round(x: Real): LongInt; - функция относится к классу функций преобразования типов. Преобразует вещественные значения в целые по правилам округления. function Trunc(x: Real): LongInt; - преобразует вещественные значения в целые, обрезая дробную часть. Нетрудно доказать, что при х ≥ 0, round(x)= trunc(x + 0. 5), а при х < 0 - round(x)= trunc(x - 0. 5). 10.3 Вещественные выражения В выражениях вещественного типа допускаются следующие операции: + - сложение; - вычитание; * - умножение; / - деление. Наряду с вещественными операндами в вещественных выражениях могут использоваться операнды целого типа. Кроме того, рассмотренные функции Abs и Sqr при условии вещественного аргумента дают вещественный результат. Приведем функции, которые дают вещественный результат вне зависимости от аргумента: function Sin(x: Real): Real;
- вычисляют одноименные 26
function Cos(x: Real): Real;
тригонометрические функции (значение аргумента передается в функцию в радианах); function ArcTan(a: Real): Real; - вычисляет обратную тригонометрическую функцию – арктангенс (значение угла возвращается в радианах.); function Ln(x: Real): Real; - вычисляет натуральный логарифм; function Expfx: Real): Real; - вычисляет экспоненту; function Sqrt(x: Real): Real; - вычисляет квадратный корень параметра (аргумента). Необходимо отметить, что операция возведения в степень в Паскале отсутствует, но для вещественных выражений эта проблема легко решается, если воспользоваться свойствами функций Exp и Ln:
e ln( x ) = x ln( x n ) = n ⋅ ln( x ) x n = e n⋅ln( x )
или
x n = exp( n ⋅ Ln ( x ))
Хотя вещественный тип относится к скалярным, но на переменные и выражения данного типа накладываются некоторые ограничения. В частности, к вещественным аргументам нельзя применить функции порядка. - Pred и Succ. Нельзя использовать вещественные значения при индексации массивов, при описании цикла for и т. п. 10.4 Логические выражения Понятие логического выражения или выражения типа Boolean во многом базируется на понятии операций отношения. Сами эти операции используют в качестве операндов выражения практически всех скалярных и базовых типов: вещест27
венного, целого, логического, символьного и строкового. При выполнении этих операций допускается смешение типов Real (всех вещественных), Integer (всех целых). Результат операции отношения для любых операндов всегда логического типа - {false, true}. В Паскале реализованы следующие операции отношения: = - равно; <> - не равно; > - больше чем; < - меньше чем; >= - больше или равно; <= - меньше или равно. А А А А
Примеры: = В {результат равен true, если А равно В и f a l s e , е с л и А не равно В} <> В > В > 0
В операциях сравнения для символов и строк необходимо вспомнить, что у каждого элемента скалярного, перечислимого типа есть свой код - порядковый номер. Его можно получить, используя функцию Ord. Заведомо известно, что коды всех латинских и русских букв упорядочены в соответствии с латинским и русским алфавитами, а коды всех цифр упорядочены по их арифметическим значениям. Данное отношение порядка и позволяет сравнивать любые символы и строки. Пример: 'А' < ' D ' ‘X ’ < ‘Y ’ ‘ 2 ’ < ‘5 ’ ‘ ABCD’ <
‘АВСЕ’ 28
'ABC'
<
'ABCD'
Кроме операций отношения в логических выражениях используются скобки и логические связки (операции с операндами и значением типа Boolean): - унарная: not; - бинарные: and, or, xor; Запись бинарных логических связок осуществляется следующим образом: <логическое выражение - первый операнд> <логическая связка><логическое выражение - второй операнд> ; При использовании логических связок необходимо большое внимание уделять расстановке скобок так, как при совместном использовании операций сравнения и логических связок можно легко ошибиться. Логические связки имеют более высокий приоритет, чем операции сравнения. Примеры логических выражений со связками: (А>5) and (Т<10) { . . . но не А > 5 and Т < 10} ((М<3) or (Х<0)) and Z С помощью произвольных логических выражений X и Y можно легко показать смысл логических связок. Напомним, что выражения X и Y могут иметь только одно из двух значений {false, true}. X and Y - ("конъюнкция") данное логическое выражение истинно, тогда и только тогда, когда X и Y одновременно истинны. X or Y - ("дизъюнкция") данное логическое выражение истинно, когда по крайней мере одно из выражений X или Y истины. X xor Y - ("исключающая дизъюнкция") данное логическое выражение истинно, когда X<>Y и ложно в обратном случае. 29
not Х - ("отрицание") данное логическое выражение истинно, когда X— ложно и ложно, когда X— истинно. Используя операции отношения на множестве логических значений можно определить и другие операции математической логики. Например: (X, Y:Boolean) Х<= Y- импликация; Х ≡ Y— эквивалентность; Х <> Y— исключающее или. Сами логические операции очень удобно определять с помощью таблиц истинности, в которых единица соответствует логическому значению true, а ноль - false. Пусть X и Y это логические операнды. Для всевозможных комбинаций их значений построим таблицу истинности логических выражений X and Y, X or Y, X xor Y, not X, X<=Y, Х ≡ Y. X
Y
X and Y
X or Y
Х <> Y
Х<= Y
X≡Y
not Х
0
0
0
0
0
1
1
1
0
1
0
1
1
1
0
1
1
0
0
1
1
0
0
0
1
1
1
1
0
1
1
0
При анализе сложных логических выражений тоже очень удобно пользоваться таблицами истинности, предварительно разбив выражение на элементарные части. Пример: 30
Выражение (A and В) or (A and (not В)) A
B
A and B
not B
A and (not B)
(A and B) or (A and (not B))
0 0 1 1
0 1 0 1
0 0 0 1
1 0 1 0
0 0 1 0
0 0 1 1
При вычислении логических выражений можно воспользоваться установкой компилятора {$В-}/{$В+}. Она отвечает за режим оптимизации вычисления логических выражений. Например, в логическом выражении (2<>2) and (a
при работе с файлами, мы познакомимся с вводом/выводом в файл и на принтер. 11.1 Операторы ввода (Read, Readln) Read (<список переменных через запятую>); Readln (<список переменных через запятую>); На экране отображаются вводимые с клавиатуры символы, начиная с позиции текстового курсора, а курсор после вывода очередного символа перемещается на позицию вправо. Примеры: Read (А) ; Read (А, В, С); Readln (X, У, Z) ; Оператор Read отличается от оператора Readln тем, что первый из них из строки ввода данных (которая заканчивается нажатием клавиши <Enter>) вводит столько из них, сколько перечислено в списке ввода. Например, в программе имеется оператор Read (i, j), а на клавиатуре мы набрали следующие данные: 4 5 7 <Enter> . Переменным i и j будут присвоены значения 4 и 5. Однако при следующем выполнении оператора Read (i, j) независимо от того, что мы наберём на клавиатуре, переменной i будет присвоено значение 7. Оператор Readln, независимо от длины списка ввода и длины строки ввода всегда игнорирует «лишнюю» информацию и ожидает нажатия клавиши <Enter>, даже если список ввода пуст. Поэтому, чтобы удержать экран пользователя перед глазами, полезно, перед завершением работы программы, вставить в неё оператор “Readln;” . 11.2 Операторы вывода (Write, Writeln) Write(<список выражений, через запятую>); Writeln(<список выражений, через запятую>); На экран выводятся значения выражений, начиная с текущей позиции текстового курсора. Cледует различать термины 32
"переменная", "значение" и "выражение". "Выражение" - это наиболее общее понятие. "Значение" и "переменная" это тоже выражения, только простейшие. Основное отличие оператора Write от Writeln в том, что после вывода на экран значений выражений оператором Write текстовый курсор остается в конце выведенной цепочки символов, а после вывода оператором Writeln курсор переходит на начало следующей строки. Операторы Write и Writeln предоставляют возможность форматировать значения выражений различных типов при выводе их на экран: < OutExpr > [: [: ] ] . Здесь OutExpr – выводимое выражение, n1 , n2 - выражения типа WORD (квадратные скобки означают возможность отсутствия заключенных в них параметров). Подпараметр n1, если он присутствует, указывает минимальную ширину поля, в которое будет записываться символьное представление значения OutExpr. Если символьное представление имеет меньшую длину, чем n1 , оно будет дополнено слева пробелами, если большую длину, то подпараметр n1 игнорируется и выводится необходимое число символов. Подпараметр n2 задает количество десятичных знаков в дробной части вещественного числа. Он может использоваться только совместно с n1 и только по отношению к выводимому выражению одного из вещественных типов. Если ширина поля вывода не указана, соответствующий параметр выводится вслед за предыдущим без какого-либо их разделения. Символы и строки передаются выводимому файлу без изменений, но снабжаются ведущими пробелами, если задана ширина поля вывода и эта ширина больше требуемой для вывода. При выводе логических выражений в зависимости от их значения выводятся строки TRUE или FALSE. (Ввод логических констант процедурами READ или READLN не предусмотрен). 33
Вещественные числа выводятся в экспоненциальном формате, если не указан подпараметр n2 . Экспоненциальный формат представляет вещественное число в виде _s#.##############E*####, где знак «_» - пробел; s - пробел для положительного и знак «-» для отрицательного чисел; # десятичная цифра; Е - символ десятичного основания; * - знак «+» или «-» в зависимости от знака десятичного порядка числа. Если подпараметр n1 опущен, принимается его значение по умолчанию 23. Если n1 меньше 10, считается, что он равен 10. Например, число -120.3 с параметром n1=12 будет выглядеть на экране таким образом: -1.203Е+0002 . Если подпараметр n2 для вещественных выражений равен нулю, ни дробная часть числа, ни десятичная точка не выводятся. При отрицательном значении n2 этот параметр игнорируется и число выводится в экспоненциальном формате с учетом n1. Если значение n2 больше 18, принимается значение 18. Следует учесть, что при указании подпараметра n2 вещественное число всегда будет выводиться в формате с фиксированной точкой и требуемым количеством знаков в дробной части, даже если значение подпараметра n1 окажется недостаточным для размещения целой части. В этом случае значение n1 автоматически увеличивается. Если OutExpr в нижеследующем выражении < OutExpr > : , является выражением целого, строкового или булевого типа, то значение переменной прижимаются к правому краю поля вывода. Если n1 отрицательно, то значение выражения OutExpr прижимается к левому краю. Примеры: Writeln (A: 6: 3) ; Write(M: 7: 3, Т: 4: 2, В: 7: 4); В дальнейшем мы познакомимся с типом String ,, а сейчас уточним, что операторы Write и Writeln позволяют выводить на 34
экран строковые значения. Сами строковые значения заключаются в апострофы: 'ABCDF', 'мама мыла раму'. Пользуясь этим мы можем выводить на экран различные сообщения и комментарии, чередуя в случае необходимости их со значениями выражений других типов. Примеры: 1. Writeln('А=', А); { - на экран будет выведена строка 'А=' и значение переменной А: А=75} 2. Write('число простое ') 3. Writeln (' Yes ') 12 Оператор присваивания В Паскале разделитель в операторе присваивания состоит из двух символов (:=) и отличается от символа операции сравнения (=). Это различие существенно облегчает привыкание к оператору и заставляет перед использованием задумываться о его смысле начинающих программистов, пьггающихся перевести свои знания математической символики в алгоритмический язык. Структура оператора присваивания: <идентификатор — имя переменной>:= <выражение соответствующего типа>: Примеры: А:= 5; B:= 3.7; N:= Sqr(T) + Sqr(U); M:= 25 * Т - 80 * L; Выполнение оператора присваивания заканчивается тем, что изменяется правая часть, а именно, значение выражения в левой части помещается в участок памяти, выделенный под переменную в правой. Обычно ошибки, возникающие при компиляции оператора присваивания связаны с несоответствием типа выражения типу переменной (Type mismatch), а ошибки во время выполнения возникает из-за неправомочности операции в выражении: де35
ление на ноль (Division by zero), неправильная операция с плавающей точкой (Invalid floating point operation). 13 Составной оператор Часто возникает необходимость объединения нескольких операторов в один (тело процедуры, тело оператора ветвления, тело цикла и т. д. ). Для реализации этой потребности в Паскале существует возможность организации составного оператора, имеющего следующую структуру. begin <оператор 1>; <оператор 2>; ………………. <оператор n> ; end; Примеры: 1) begin writeln(A[i] ) ; max:= A [ i ] ; inc(t); k:= i; end; 2) begin writeln('да'); n : = 1000; end; Иногда, ключевые слова begin – end называют операторными скобками, так как окаймляют операторы внутри себя аналогично скобкам в арифметических выражениях.. 36
Простым случаем составного оператора является пустой составной оператор: begin end; Часто к его помощи прибегают при написании больших программ, когда некоторые ее участки детализируются по мере отладки. 14 Использование стандартных процедур и функций 14.1 Понятие формальных и фактических параметров Для дальнейшей работы на языке Паскаль нам необходимо познакомиться с понятием подпрограмм: процедур и функций, правилами описания их заголовков и способами передачи данных из программы в подпрограмму и обратно. Основное отличие процедур и функций заключается в том, что идентификатор функции имеет (или возвращает) значение определенного типа и поэтому для вызова функции необходимо включить ее в выражение, например в правую часть оператора присваивания. Примеры: p:=sin(x); {sin - идентификатор функции} t:=2*round(w)-7; {round - идентификатор функции} В последствии, при знакомстве с новыми функциями и процедурами, мы будем записывать их заголовки, поэтому сразу отметим, что заголовок функции записывается следующим образом: function <идентификатор-имя процедуры>(<список параметров>): <тип значения функции>; Примеры: function Chr{X: Byte): Char; function Copy(S: String; Index: Integer; Count: Integer): String; 37
function GetArgStr (Dest: PChar; Index: Integer; MaxLen: Word): PChar; Процедура, в отличие от функции, не может быть составной частью выражения и вызывается самостоятельным оператором. Примеры: Writeln(А,В,С); {Writeln-идентификатор процедуры} Read(R); {Read - идентификатор процедуры} Заголовок процедуры записывается следующим образом: procedure <Идентификатор - имя процедуры>(<список параметров>; Примеры: procedure Delay (MS: Word) ; procedure Delete(var S: String; Index: Integer; Count: Integer); procedure GetDate(var Year, Month, Day, DayOfWeek: Word); Необходимо отметить, что списки параметров в заголовках процедур могут отсутствовать. Пример: procedure ClrScr, procedure NoSound; procedure CloseGraph; При работе с функциями и процедурами следует различать формальные и фактические параметры. Формальные параметры - это параметры, используемые при описании заголовков и самих подпрограмм, а фактические - это те параметры, которые мы передаем подпрограмме при ее вызове. Примеры: function sin(x: Real): Real; Здесь переменная х использована для описания заголовка функции, поэтому мы говорим, что этот параметр формальный. t : = sin(3.14); В этом примере параметр 3.14 используется в выражении при вызове функции, значит это фактический параметр. 38
t:= sin(u); Последний пример отличается от предыдущего только тем, что на месте значения стоит переменная. Переменная и - фактический параметр. Существует и другая классификация параметров подпрограмм: параметры-переменные и параметры-значения. При описании заголовка перед параметром-переменной ставится служебное слово Var. Основное отличие параметров-переменных от параметров-значений заключается во внутренней реализации передачи данных в подпрограмму, а для нас важно, что: - на месте формального параметра-переменной при вызове подпрограммы может быть только переменная, передача значения или выражения приведет к ошибке; - если на место формального параметра переменной подставить фактический параметр, то изменившись в подпрограмме он вернется измененным в то место программы, из которого она была вызвана. Пример: procedure GetDate(var Year, Month, Day, DayOfWeek: Word); В процедуре GetDate параметры Year, Month, Day, DayOfWeek являются параметрами переменными, через которые процедура возвращает данные - результат ее выполнения. При вызове данной процедуры фактическими параметрами могут быть только переменные. В обратном случае вызов процедуры приведет к ошибке. 14.2 Некоторые полезные процедуры и функции Библиотечный модуль ( в дальнейшем просто модуль) содержит описания и подпрограммы (процедуры и функции), которые могут использоваться пользователями. Имя этих модулей CRT, SYSTEM, DOS и многие другие. Все стандартные процедуры и функции находятся в определённых модулях. Следовательно, чтобы использовать требуемую функцию в своей программе, необходимо подключить соответствующий 39
модуль оператором uses c указанием имени модуля. Этот оператор необходимо поставить перед описаниями, то есть после оператора program. Например: Uses CRT; При графическом изображении алгоритмов у нас часто возникала необходимость заканчивать работу алгоритма в различных частях его изображения. Имеется специальная процедура halt, которая позволяет аналогично в любом месте программы на языке TURBO PASCAL закончить её выполнение. Полезными являются процедуры Inc и Dec. Обе изменяют значение целой переменной по следующему правилу: Inc(i) соответствует оператору i: = i+1 , Dec(i) соответствует оператору i: = i -1 . Для работы с переменными перечислимых типов применяются следующие стандартные функции: Succ(<значение перечислимого типа>); - возвращает следующий за данным элемент. Например, Succ(plus) вернёт значение minus. Если i - переменная целого типа, то succ(i) дает следующее значение, то есть (i+1) . Ргеd(<значение перечислимого типа>); - возвращает предшествующий данному элемент. Pred(minus) вернёт значение plus . Если i - переменная целого типа, то Pred(i) дает предыдущее значение, то есть (i-1) . Ord(<значение перечислимого типа>); - возвращает порядковый номер данного значения в типе. Тип результата Longint . Порядковый номер первого элемента равен нулю (исключение составляют целые типы: порядковый номер первого значения типа Integer нулю не равен). При вызове функции Chr возвращается символ, имеющий данный порядковый номер. Описание этой функции имеет вид : function Chr(X: Byte): Char ; Тип результата выполнения первых двух функций совпадает с типом аргумента, а результат выполнения функции Ord имеет тип Integer. Приведём примеры применения этих 40
функций. Значения следующих выражений будет True: • Succ(5)=6 • Pred(5)=4 • Ord(False) = 0 • Ord(True) = 1 • Succ(False) = True • Pred(True) =False • Ord('A')=65 • Chr(65) = 'A' . GetTime(Hour, min, sec, hand) – процедура помещает в соответствующие переменные текущие значения времени (час, минута, секунда, количество миллисекунд. Все переменные типа Word. Эту процедуру содержит модуль DOS. Модуль CRT содержит функцию Readkey (без параметров), которая при обращении к ней ждёт ввода символа. После нажатии любой клавиши её код (клавиши) возвращается функцией. При этом значение клавиши не отображается на экране и курсор не сдвигается. Задачи для самостоятельного решения 1. Даны два числа. Вывести на экран их сумму, разность и произведение. 2. Даны катеты прямоугольного треугольника. Найти его гипотенузу и площадь. 3. Найти координаты точки (х, у), делящий отрезок АВ пополам, если известно, что его концы имеют координаты A(x1 , y1), B(x2, y2). 3. Даны отличные от нуля действительные числа х и у Вывести | x|−| y| x2 − y2 на экран значения выражений и 2 . 3+ | xy | x + y2 5. Даны действительные числа х, у, z. Вычислить значение вы-
41
ражения
| x −1| − | y | 1+
2
2
x y + 2 4
+ x(sin z + e −( x + z ) .
15 Условный оператор (ветвление)
При написании программ на языке Паскаль существует возможность использования полного и неполного ветвления. Полное: If <выражение типа Boolean - условие> then <оператор 1> else <оператор 2>; Следует отметить, что в случае ветвления после первого оператора перед "else" символ разделитель -";" не ставится. Неполное ветвление: if <выражение типа Boolean - условие> then <оператор>; Примеры: if a > b then a:= 75 else b:= 75; if (a<0) or (b<0) then a:= -934; Если возникает необходимость в одном условном операторе выполнить несколько операторов подряд, то они объединяются в составной оператор. Примеры: 1) if a[i] > max then begin max:= a[i]; num:= i; end; 2) if (a
m:= a; end else begin writeln('l= ', l); r := l; m: = t ; n: = у; end; Задача. С клавиатуры вводятся три числа, вывести их на экран в порядке возрастания. program р6; var xl, х2, хЗ: Real; begin WriteLn('Введите три числа'); Read{xl, x2, хЗ); Writeln('Числа в порядке возрастания: '); if (xl <= x2) and (xl <= хЗ) then if x2 < хЗ then Writeln(xl, x2, x3) else Writeln(xl, x3, x2); if (x2 < xl) and (x2 < x3) then if xl < x3 then Writeln(x2, xl, x3) else Writeln(x2', x3, xl) ; if (x3 < xl) and (x3 < x2) then if xl < x2 then Writeln(x3, xl, x2) else Writeln{x3, x2, xl); end. Задача. Написать программу для решения квадратного уравнения вида: а-х2 +b х + с = 0 . program p7; var a, b, с, d, xl, x2: Real; 43
begin Writeln('Введите коэффициенты уравнения'); Read (a, b, с) ; d:=b*b-4*a*c; if d < 0 then Writeln('корней нет') else begin xl:= (- b + sqrt(d)) / (2 * a); x2:= (- b - sqrt(d)) / (2 * a); Writeln ('корни уравнения: '); Writeln('X1=' , x1: 6 : 3, ' x2=' , x2: 6: 3); end; end. 16 Оператор множественного выбора (варианта) - case
Ситуация множественного выбора возникает при написании программ достаточно часто: обработка нажатия клавиш, обработка списков, файловых потоков и т. д. В этом случае использование обычного ветвления затруднительно очень много операторов ветвления потребуется включить в программу. Оператор варианта позволяет достаточно компактно решить подобные задачи. Форма записи оператора case: case <выражение перечислимого типа> of <список значений и диапазонов 1>: <оператор 1>; <список значений и диапазонов 2>: <оператор 2>; ……………………………………………………….. <список значений и диапазонов n>: <оператор n>; else <оператор n+1>; end; 44
После выполнения одного из операторов следующие за ним условия не проверяются. В структуре оператора case допускается использование альтернативы else, если во всех списках значений и диапазонов не встретилось данное значение исходного выражения, то программа будет выполняться по ветке else. Примеры: 1) case A of 1, 2, 3: n:= 75; 4 . . 8: n:= 134; 5 . . 10, 17 . . 23: n:= 247 ; end; 2) case chr(M) of 'A', 'Б', 'Е', 'Р’: begin writeln(M); Inc(X); end; 'Л’ , 'K', ‘T’, 'И' : begin writeln(M-32) ; Inc(Y); end; 'Ь’, 'Ю’, 'Э' : begin writeln(M-27) ; Inc (Z) ; end; end; 3) case V of 1, 2, 7: Write ('Да') ; 4, 6, 8: Write ('Может быть’); else Write ( 'Нет' ) ; end; Задача. С клавиатуры вводится число 0 ≤ п ≤ 5. Вывести на экран числительное, соответствующее этому числу. program p8 ; var 45
n: integer; begin Writeln (' Введите число’); Read (n) ; case n of 1 : write ( ' один' ) ; 2: write (' два ' ) ; 3 : write ( ' три' ) ; 4 : write ( 'четыре' ) ; 5: write ( ' пять ' ) ; else write ('число не подходит'); end; end. Задачи для самостоятельного решения 1. Дано действительное число х. Вычислить значение величины ⎧ x 2 + 4 x − 7 при x ≤ 2 ⎪ y=⎨ . 1 ⎪⎩ x 2 + 4 x − 7 при x > 2
2. Числа а и b выражают длины катетов одного прямоугольного треугольника, а с и d - другого. Выяснить, являются ли треугольники подобными. 3. Вычислить, если это возможно, значение выражения x+ y . ( x − 2)( x + 7) 4. Написать программу, которая по номеру месяца определяет, к какому времени года он относится. 5. Написать программу, которая запрашивает название месяца и выводит на экран количество дней в нем.
46
17 Циклы
Паскаль предлагает три различных оператора цикла, которые применяются в определенных ситуациях. 17.1 Цикл типа for Этот цикл принято использовать, когда известно количество итераций и на каждом шаге требуется знать ее номер или соответствующий ей элемент порядкового типа. В цикле for используется счетчик, который увеличивается или уменьшается автоматически. Существуют две формы цикла — прямая и обратная. Прямая: for <идентификатор_переменной порядкового типа>:= to do <оператор>; n1 - меньшее значение соответствующего типа, n2 большее значение соответствующего типа. Если при использовании прямого цикла начальное значение управляющей переменной (счетчика) больше конечного, то тело цикла вообще не выполняется. Примеры: 1) for i:=1 to 100 do S:= S + i*i;
2) for c:= 'A' to 'Z' do Writeln(ord(c), ' - ', c) ; 3) for t:= 300 to 400 do if t mod 3= 0 then Writeln(t); Обратная: for <идентификатор переменной порядкового типа>:= downto do <оператор>; nl — большее значение соответствующего типа; n2 - меньшее значение соответствующего типа. 47
Примеры: 1) for i:= 100 downto 1 do p:= p * i; 2) for c:= 'Я’ downto 'A' do if t = с then Writeln('Входит'); 3) for t:= 300 downto 400 do v:= v - t mod 3; Как вы заметили, в теле цикла может выполняться только один оператор, который записан непосредственно за структурой for .. do. Поэтому, если возникает необходимость, мы можем воспользоваться составным оператором и объединить в теле цикла несколько операторов. Примеры: 1) for i:= I to 100 do begin Writeln(i); S:= S + i*j ; Writeln(S); end; 2) for h:= 'F’ to 'X’ do begin read(b, c); 1:= 1 + chr(ord(t) - ord(h)); m:= m + chr(ord(c) + ord(h)); writeln (1, m); end; Как уже было сказано, счетчиком цикла могут быть переменные любого порядкового типа, даже если это тип пользователя. Пример: program р9; type Month= (Jan, Feb, Mar, Apr, May, Jun, 48
Jul, Aug, Sep, Oct, Nov); var i: Month; z: integer; begin z:= 0; for i:= Jan to Sep do Z:= Z + Ord(i)*32; Writeln(z); end. Советы для начинающих 1. Постарайтесь не изменять счетчик цикла в самом цикле. Если хорошо подумаете, то поймете, что в этом нет необходимости, а последствия этого могут оказаться непредсказуемыми. 2. В Паскале нет понятия - шаг цикла (step), вещественный тип не может быть типом счетчика цикла, так как не является перечислимым, но при возникшей необходимости в цикле можно воспользоваться любой вещественной переменной. Для этого на каждой итерации необходимо изменять ее на определенное вещественное значение - шаг. 3. Перед использованием цикла постарайтесь точно понять, «почувствовать» как он работает, что происходит со счетчиком, сколько раз он выполняется, как изменяются переменные в выражениях типа S: = S +..., Р:= Р *. . . и т. п. Их корректность зависит от построения выражения и начальных значений переменных. Например: р: = 0; for i:= 2 to n do p:= p* i; Стандартная ошибка. В этом примере незадачливый, начинающий программист попытался решить задачу нахождения факториала, а получил ноль. Задача. Табулировать функцию f(x) на отрезке [а,b] с шагом h. program p10, 49
var a, b, h, x, y: Real; i, n: integer; begin Writeln('введите начало и конец отрезка’); Read(a, b); Writeln('введите шаг'); Read(h); х:= а; {начальное значение переменной х} n:=round((b-a)/h); {количество полных интервалов} for i:= 1 to n do begin y:= sin(x); {находим значение функции, для текущего значения аргумента} Writeln(i, ' х= ' , х, ' у= ' , у) ; {выводим на экран номер шага, значение аргумента и значение функции} x:=x+h; {увеличиваем аргумент на значение шага} end; end. n
Задача. Найти
∑ i =1
sin(i ) . 2i
program p11; var S, f: real; i, n: integer; begin Writeln('введите n’); Read(n); f:= 1; S:= 0; {начальное значение переменных f и S} for i:= 1 to n do begin 50
f: = f*2; S:= S + sin{i)/f; end; Writeln(' S= ', S: 6: 4) ; end. n
Задача. Найти
∏ (sin(a ) + cos(b )) , n, а и b вводятся с i
i
i =1
клавиатуры. program p12; var a, b, P, k, 1: real; i, n: integer; begin Writeln('введите a, b и n'); Read (a, b, n); k:= 1; 1:= 1; P:= 1; {начальное значение переменных k,1 и Р} for i:= 1 to n do begin k:= k * a; 1:= 1 * b; P:= P * sin(k) + cos(i); end; Writeln(‘ P= ', P: 6: 4); end. Часто возникает необходимость организации вложенных циклов, действие которых можно понять при анализе работы следующей программы: program р13; var i, j : integer; begin for i:= 1 to 5 do 51
for j:= 1 to 5 do writeln ('i= ', i, ' j= ', j); end.
Программа выводит на экран всевозможные пары значений целых чисел до пяти. i-1 j=1 i=l j=2 i=l j=3 i=l j= 4 i=l j= 5 ……… ……… i=5 j= 2 i=5 j= 3 i=5 j= 4 i=5 j= 5 Каждая строка, выводимая на экран, соответствует одному выполнению тела цикла. Задача: Найти
n
n
i =1
j =1
∑ ∑ sin( i
j
) cos( i j ) .
program p14 ; var S : real ; i, j, ij : integer; begin ij:= 1; S:= 0; {начальное значение переменных S и ij} for i:= 1 to 10 do begin ij := i; for j:= 1 to 10 do begin ij : = ij * i; S:= S + sin(ij)*cos(ij); 52
end; end; Writeln(' S= ', S: 6: 4); end. 17.2 Цикл типа While Этот цикл используется, если известно условие прекращения цикла, но неизвестно количество итераций. While <выражение типа Boolean> do <оператор>;
В данном случае <выражение типа Воо1еап> - это условие продолжения цикла (пока условие - истинно, тело цикла выполняется). Если условие принимает с самого начала значение false, то тело цикла вообще не выполняется. Конструкция цикла предусмотрена на случай того, что телом является только один оператор, который непосредственно следует за структурой While... do, а для включения в тело цикла нескольких операторов необходимо объединить их в один составной. Примеры: 1) While x < у do х; = х - 1 ; 2) while (a < m) or (b < n) do begin a:= a - t;b:= b - z; end; При организации цикла While может возникнуть ситуация "зацикливания". Чтобы этого не произошло, по крайней мере, один из повторяемых в теле цикла операторов должен 53
влиять на значение условия прекращения цикла, иначе он будет выполнятся бесконечно. Примеры: 1) While 2=2 do Writeln('2=2') ; 2) while (a<100) or (a>100) or (a=100) do Writeln(a); Задача. Используя алгоритм Евклида, найти НОД (наибольший общий делитель) двух чисел. Алгоритм Евклида: 1) пока переменные неравны, заменить большее значение на разность большего и меньшего; 2) выбрать любое значение в качестве результата. program p15; var а, b, х, у: integer; begin Writeln('Введите а и b'); Read (a, b); х:= а; {запоминаем начальные значения а и b} у:=b; While a <> b do if a > b then a:= a - b else b:= b – a; Writeln (‘ HOД( ‘, x, ', ' , y, ') = ' , a); end. 1 1 Задача. Найти сумму ряда 1 − 2 + 2 − ... с заданной 2 3 точностью – е. Первый отброшенный элемент ряда не должен превосходить е. Вывести на экран количество слагаемых, суммированных до достижения необходимой точности. program р16; var S, е, х: real; 54
z, k: integer; begin Writeln('Введите точность'); Read(e); S = 0; x = 1; z = 1; k = 1; while x > e do begin S:= S + z * x; k:= k + 1; x:= 1 / Sqr(k) ; z:=-z; end; Writeln('S= ', S, ' k= ', k); end. 17.3 Цикл типа Repeat... Until Этот цикл во многом похож на цикл While, но отличается от него тем, что условие проверяется не в начале цикла, а в конце, и это не условие продолжения цикла, а условие выхода из него. В отличие от цикла While, тело цикла Repeat... Until всегда выполняется по крайней мере один раз. Repeat <оператор 1>; <оператор 2>; ………………. <оператор n>; Until <выражение типа Boolean>; В данном случае <выражение типа Boolean> - это условие выхода из цикла (пока условие - ложно, тело цикла выполняется). Конструкция цикла такова, что для организации тела 55
не требуется объединять операторы в один составной, потому что часть программы между служебными словами Repeat и Until и так является его телом. Примеры: 1) Repeat x: = x + 1 ; Write('х= ', х); У:= sin(х); Write('у= ', у); Until x= 100; 2) Repeat t:= t - 1; Until t= 0; ∞
Задача. Найти ∑ i =1
1 с точностью e. i3
program p17; var S, е, х: real; i: integer; begin Writeln('Введите точность'); Read(e); S:= 0; i:= 1; Repeat x:= l/(i*i*i) ; S:= S + x; i:=i + 1; Until x < e; Writeln(‘S= ' , S: 10: 8) ; end. 56
17.4 Дополнительные операторы при программировании циклов Иногда необходимо бывает проводить проверку на возможный выход из цикла где-нибудь в его середине, а не в начале или конце. Такой выход из цикла обеспечивается процедурой break, которая прерывает выполнение самого внутреннего вложенного цикла, будь то for, while или repeat. Также не редко полезной может оказаться процедура continue, которая прерывает выполнение самого внутреннего цикла for, while или repeat и передаёт управление на его заголовок, так что начинается выполнение очередной итерации цикла. Пример: …………………… while abs(p) > e do begin …………………. for i : = 1 to m do begin ………………………… if y <= 0 then continue; ………………………… p: = p+y; end; if p > 1 then break; …………………………… s: = s+p; end; ……………………… В этом примере во внутреннем цикле пропускаются итерации в случае отрицательных или равных нулю величин y, а во внешнем – цикл прервется, если величина p превысит 1. Задачи для самостоятельного решения 57
n
1. Дано натуральное число n. Вычислить
2k
∑ (k + 1)(k k =1
2
+ k)
.
2. Дано натуральное число n. Вычислить n
∑ k =1
k!+ k n − k +1 k
7 k k + C (i + 2)
.
i =0
3. Найти значения полинома у = 8m + 4.3m2 -1,46m3 для 1.0 ≤ m ≤ 5.9 с шагом hm = 0.1 . 4. Еще один алгоритм Евклида для нахождения НОД неотрицательных целых чисел основан на следующих свойствах этой величины. Пусть m и n - одновременно не равные нулю целые неотрицательные числа и пусть m ≥ n. Тогда, если n=0, то НОД(m,n)=m, а если n ≠ 0, то для чисел n, m и r , где r - остаток от деления m на n, выполняется равенство НОД(m,n)=НОД(n,r). Например, НОД(15,6)=НОД(6,3)=НОД(3, 0)=3. Даны натуральные числа n, m. Используя алгоритм Евлида, найти их наибольший общий делитель. 5. Вычислить y = 3 + 6 + ... + 96 + 99 . 6. Дано
действительное число х ≠ 0 . Вычислить функ3 5 7 цию y = x − 3 + 5 − 7 + ... . Вычисления прекратить, когда x x x очередной член будет по абсолютному значению меньше 1 0-6 . 7. Дано натуральное число n. Вычислить сумму и произведение его цифр. 8. Дано натуральное число n >2. Вывести на экран все простые числа из диапазона [2,n]. 18 Массивы Массивом называют последовательность однотипных данных, имеющих один идентификатор - имя и различающихся по номерам (индексам) в этой последовательно58
сти.
Чтобы задать массив необходимо указать имя массива, тип его элементов, количество индексов (размерность массива), диапазоны изменения индексов. 18.1 Одномерные массивы Описание одномерных массивов имеет вид
<список идентификаторов>: array [<диапазон изменения индексов>] of <тип элементов массива>; <диапазон изменения индекса> - это поддиапазон любого перечислимого типа (1 .. 10, Mon .. Sat, ‘A’ .. ‘Z’ и др.). Типом элементов массива может быть любой простой тип. Кроме того, элементами массива могут быть другие массивы. Переменные массивного типа описывают, как и остальные переменные в разделе Var. Примеры: A: array[l ..10] of integer; {массив с индексами от 1 до 10, элементы массива целые числа} В: array['А' . . ' Z ' ] of Real; {массив с индексами от 'А' до ‘Z’ , элементы массива - вещественные числа} С: array[10 .. 20] of Char; {массив с индексами от 10 до 20, элементы массива - символы} D: array[1..3] of array[1..2] of real; {массив с индексами от 1 до 3, его элементами являются другие массивы}
Если два массива А и В однотипные, то допускаются следующие операторы присваивания: А: = В; В: = А; Необходимо отметить, что элементы массивов имеют те же свойства, что и обычные переменные соответствующих ти59
пов. Доступ к элементу массива в программе осуществляется по его номеру — индексу, который указывается в квадратных скобках. Примеры: 1) A[1]: = 7; A[2]: = A[6]; A[6]:=AQR(f); На месте индекса элемента массива может стоять идентификатор переменной . В этом случае во время выполнения программы будет осуществлена подстановка – значение этой переменной. Примеры: 1) i:=7; A[i] := 23; {-> А[7] : = 23;} 2) for j:= 1 to 10 do A[j]:= j*j; Одномерный массив можно представить как вектор c элементами (А1, А2,..., АN). Часто приходится вводить элементы массива с клавиатуры, для этого проще всего использовать цикл: for i: = 1 to n do read (A[i]) ;
или с выводом сообщения: for i:= 1 to n do begin Writeln('введите А[' , i , ']'); Readln(A[i] ) ; end; 60
Подобным образом можно организовать вывод элементов массива на экран. Например, в строку без комментариев: for i:= 1 to n do Writeln(А[i] : 4);
или в столбец и с комментариями: for i:= 1 to n do Writeln('А[' , i , ']= ' , A[i]);
Как правило, и любые другие операции с элементами массива осуществляются в цикле, и лучше всего для этих целей подходит цикл типа for … do. Часто размер массива приходится несколько раз изменять во время отладки программы. Изменение диапазона в описание массива влечёт за собой изменение параметров цикла и возможно других переменных. Можно облегчить подобные преобразования, если при указании диапазона изменения индексов использовать нетипизированные константы. Задача. Ввести массив с клавиатуры, найти максимальный и минимальный элементы. program p18; const n = 10; var A: array[1, n] of real; i: integer; max, min: real; begin {ввод элементов массива} Writeln(‘введите элементы массива, ‘ , n, ‘ элементов'); for i:= 1 to n do Read (A [i] ); {поиск максимума и минимума} max:= А [ 1] ; min:= A [ l] ; for i:= 2 to n do begin 61
if A[i] > max then max:= A[i] ; if A[i] < min then min:= A[i]; end; {вывод максимума и минимума} Writeln('max= ', max, ' min= ‘ , min); end. Задача. Элементы массива вводятся с клавиатуры, заменить в нем все отрицательные элементы их квадратами, а положи-тельные - кубами. Вывести на экран исходный и новый масси-вы. program p19; const n = 10; Var А, В: array[ 1.. n] of real; i: integer; begin {ввод элементов массива} Writeln(‘введите элементы массива, ‘ , n, ‘ элементов'); for i:= 1 to n do Read (A [i] ); B:=A; {запомнили исходный массив } For i:= 1 to n do if A[i] < 0 then A[i] := sqr(A[i]) else if A[i] > 0 then A[i] := sqr (A[i] ) * A[i] ; {вывод элементов массивов} Writeln('Исходный массив: ') ; for i:= 1 to n do Write (B[i] : 8: 4) ; Writeln; Writeln('Новый массив: '); for i:= 1 to n do Write(A[i]: 8: 4) ; 18.2 Сортировка одномерного массива Сортировкой называют набор операций, упорядочивающий массив в соответствии с индексами, на множестве 62
которых изначально определено отношение порядка. Вопрос о возрастании или убывании упорядоченного массива для нас не принципиален. Как правило, программы, сортирующие массив по возрастанию, легко изменяются для сортировки по убыванию. Существует множество алгоритмов сортировки (пузырьковая сортировка, сортировка по дереву, быстрая сортировка, сортировка слиянием, сортировка Шелла и т. д.), они имеют большую практическую значимость, являются фундаментальными в некоторых областях информатики. В данном пособии мы приводим не самый лучший, но, безусловно, самый распространенный и очень понятный алгоритм пузырьковой сортировки. Название «Пузырьковая сортировка» происходит от образной интерпретации, по которой алгоритм заставляет «легкие» элементы мало-помалу всплывать на «поверхность». Суть алгоритма такова. Начиная с первого, сравниваются два соседних элемента массива A[i] и A[i+l], если A[i] > A[i+1], то элементы меняются местами. В первый раз мы проходим массив начиная с индекса 1, до индекса n - 1, во второй с 1 до n - 2 и т. д. Любой массив будет отсортирован за n проходов. Таким образом, порядок сложности данного алгоритма (максимальное количество операций проверок и перестановок 2 элементов массива) пропорционален n /2, что характерно для многих алгоритмов сортировки, хотя массив может быть отсортирован уже после первого прохода. Наиболее очевидное усовершенствование данного алгоритма состоит в том, что можно следить за перестановками: если при очередном проходе не было перестановок, значит массив уже упорядочен и требуется завершить сортировку. program p20; const n= 10; var 63
a: array [1.. n] of Real; i, j : integer; temp: Real; Change: Boolean; begin {ввод элементов массива} Writeln (' введите элементы массива, ', n, ' элементов ' ) ; for i := 1 to n do Read (A [i] ) ; {сортировка} j:= 0; repeat change := false; j := j + l; for i : = 1 to n - j do if A[i] > A[i + l] then begin change := true; {... если A[i] > A[i+l]} {то элементы меняются местами} temp : = A [i+1] ; A[i+1] := A[i]; A[i] := temp; end; {прекращаем сортировку, если не было перестановок} until not change; {выводим на экран отсортированный массив} for i:= I to n do Write (A [i] : 6: 2) ; Writeln; end. 18.3 Массивы с большей размерностью Мы уже знаем, что возможна организация массива массивов, а точнее элементами массива могут быть другие массивы. Описание массива в этом случае может выглядеть так: ...: array[... ] of array... 64
Подобная запись достаточно громоздка (несколько раз записывается служебное слово array и т. д. ), но синтаксис языка Паскаля позволяет описывать многомерные массивы проще. <список идентификаторов — имен, через запятую>: array [<список поддиапазонов, через запятую>] of <тип простых элементов>; Примеры: 1) М: array[1.. 3] of array [1.. 3] of Real; Подобное описание эквивалентно следующему: М: array [1.. 3, 1.. 3] of Real; В первом случае доступ к простому элементу осуществляется так: M[i][j], а во втором M[i, jl . 2) Т: array[1 .. 2, 1 .. 3, 1 .. 4] of Integer; U: array[1 .. 10, 'A1 .. 'Z'] of char; Работа с n-мерными массивами заставляет программиста организовать n вложенных циклов. Подробнее остановимся на двумерных массивах. Двумерные массивы используются, в основном, для определения матриц с индексами, изменяющимися по строкам и по столбцам. А[1, 1] , А[1, 2] , . . . , A[l, N] А[2, 1] , А[2, 2] , . . . , А[2, N] А[М, 1] , A[M, 2] , . . . , А[М, N]
С элементами двумерных массивов можно работать, указывая два индекса (номер строки и номер столбца) через запятую в квадратных скобках. Примеры: М[1, 2] := 7; 65
М[7, 5] :=46; M[i, j]:= 75; M[k, 1] := SQR(M[i,
j]
+ M[j,
i] ) ;
Ввод элементов двумерного массива по строкам: for i := 1 to n do for j:= 1 to m do Read (M[i, j]);
или с сообщениями: for i:= 1 to n do for j:= l to m do begin Write('введите М[', i, ‘, ‘ , j, '] '); Readln(M[i, j]); end;
Вывод элементов двумерного массива в виде матрицы: for i:= 1 to n do begin for j:= 1 to m do Write(M[i, j] : S: 3) ; Writeln; end; Часто при работе с двумерными массивами (матрицами) приходится оперировать с элементами, обладающими некоторыми признаками, в частности, связанными с положением элементов относительно диагоналей матрицы. Например, элемент находится на главной диагонали рисунок 1a, на побочной диагонали рисунок 2б, ниже главной диагонали, ниже побочной и т. д. (рисунки 1в÷ж).
a)
б)
в)
г) 66
д)
е) Рисунок 1
ж)
Положение этих элементов может быть описано следующими математическими отношениями: - на главной диагонали - { M[ i , j ] | i = j} - выше главной диагонали - { M[ i , j ] | i < j} - выше главной и выше побочной диагонали – { M[ i , j ] | i < j } ∩ { M[ i , j ] | i
for j:= 1 to n do if i = j then... . Но необходимости во вложенном цикле нет, достаточно одного} k:= 0; for i:= 1 to n do if M[i, i] > 0 then begin k:= k + 1; M[i, i] := 0; end; {Вывод результатов} Writeln(' k= ', k) ; Writeln; for i:= 1 to n do begin for j:= 1 to n do Write (M[i, j] : 3 ); Writeln; end; end. Задача. Матрица n*n вводится с клавиатуры, заменить все отрицательные элементы выше главной диагонали их квадратами. Вывести новую матрицу на экран. program p22; const n:= 4; var М: array[1.. n, 1.. n] of integer; i, j ; integer; begin {Ввод элементов матрицы} for i:= 1 to n do for j:= 1 to n do begin Write('введите М[ ' , i, ', ' , j , '] '); 68
Readln(M[i, j ] ) ; end; for i: = 1 to n do for j:= 1 to n do if i < j then { если выше главной диагонали} if M[i, j] < 0 then M[i, j] := SQR(M[i, j] ) ; { если элемент отрицательный, то заменить его квадратом} {Вывод результатов} for i:= l to n do begin for j:= 1 to n do Write(M[i, j]: 3 ); Writeln; end; end. 18.4 Констант-массивы Очень часто возникают сложности, связанные с вводом элементов массива, особенно во время отладки. Приходится по нескольку раз вводить большие объемы информации. Этого можно избежать, описав массив - константу в разделе Const. Одномерный массив <идентификатор — имя массива>: array [ <диапазон изменения индекса>] of <тип элементов>= (<список элементов, через запятую>); Примеры: Const A : array [ 1 .. 5] of integer = (7, 3, 22, 4, 3) ; B : array [ 1 .. 3] of real = (2.55, 7.1, 3.6) ; C : array [ 1 .. 3] of char = (‘A’, ‘B’, ‘C’) ; Многомерные массивы <идентификатор - имя массива>: array [ <список диапазо69
нов, через запятую>] of <тип элементов> = (<список элементов массива, разделённых по группам в круглых скобках>); Примеры: Const M : array [ 1 .. 3, 1 .. 3] of integer = ((1, 2, 4) , (1, 7, 8) , (22, 8, 4)) ; T : array [ 1 .. 2, 1 .. 2, 1 .. 2] of byte = (((1, 8) , (4, 4)) , ((3, 2) , (5, 4))) ; В нашем случае все массивы были описаны как типизированные константы, значит их использование в программе равноценно использованию обычных массивов - переменных. Задача. В одномерном массиве определить количество последовательностей из нулей. Последовательности - это цепочки из одного или более нулей, разделенные ненулевыми элементами. program p23; const n= 10; A: array[l..n] of Integer = (0,1,0,1,0,1,0,1,0,1) ; var zero: Boolean; i, k: integer; begin zero:= false; k:= 0; for i : = 1 to n do case A[i] of 0: if not zero then begin k:= k + 1; zero:= true; end; 1: if zero then zero:= false; end; if a[n]= 0 then k:= k + 1; Writeln(' k= ', k); end. 70
Задача. Дана матрица М — n*n. Поменять в матрице строку с номером А на столбец с номером В. Элемент на пересечении строки и столбца оставить без изменения. program p24 ; const n= 4; М: array[ l .. n, l .. n] of Integer = ( (1, 3, 5, 7), (2, 4, 6, 8), (0, 1, 2, 3), (4, 5, 6, 7)) ; var i, j, a, b, t: integer; Temp: array[1.. n] of Integer; begin Writeln('Введите номер строки а и номер столбца b'); Read(а, b); t:=M[a,b]; {- запомним элемент на пересечении} for i:=l to n do {- запомним строку в массиве Temp} Temp[i]:= M[a, i]; for i:= 1 to n do {- на место строки запишем столбец} М[а, i] := M[i, b] ; for i:= 1 to n do {- на место, столбца запишем строку} M[i, b] := Temp [i] ; M[a, b]:= t; {- восстановим элемент на пересечении} {Вывод результатов} for i:= 1 to n do begin for j:= 1 to n do Write(M[i, j]: 3 ); Writeln; end; end. Задачи для самостоятельного решения 71
1. Дан одномерный числовой массив. Определить, сколько в нем имеется пар совпадающих по величине соседних чисел; вывести на экран их индексы. 2. Дан одномерный числовой массив, все элементы которого различны. Найти наименьшее положительное значение элемента массива и его номер. Если этот номер окажется больше 3, то удвоить все элементы массива, иначе - возвести их в квадрат. 3. Даны натуральные числа n, m, действительная матрица размера n х m. Найти среднее арифметическое элементов каждого из столбцов, имеющих нечетные номера. 4. Дана целочисленная квадратная матрица порядка 8. Найти наименьшее из значений элемента столбца, который обладает наибольшей суммой модулей элементов. Если таких столбцов несколько, то взять первый из них. 5. Определить, является ли заданная целочисленная квадратная матрица порядка 9 магическим квадратом, то есть такой, в которой суммы элементов во всех строках и столбцах одинаковы. 6. Дан одномерный числовой массив. Упорядочить его элементы по неубыванию (то есть переставить его элементы так, чтобы для всех k выполнялось xk ≤ xk+1), используя алгоритм сортировки выбором: отыскивается наибольший элемент и переносится в конец массива, затем этот метод применяется ко всем элементам, кроме последнего (он уже находится на своем окончательном месте) и т. д. 19 Строки 19.1 Строковый тип Строки символов, реализованные в языке Паскаль, обладают всеми признаками и свойствами одномерных символьных массивов (... array... of Char). Символьные массивы могут входить в состав строковых выражений. В этом случае массив преобразуется в строку, длина которой равна длине массива. Символьным массивам можно присваивать значения 72
строковых констант, однако нельзя присваивать значения строковых переменных и значения строковых выражений. Пример: program p25; const S1= 'ABCDFGHIJK'; var S2: String[10]; S3: array [1.. 10] of char; begin S2:= S3; {- присваивание возможно} S3:= S1; {- присваивание возможно} S3:= S2; {- ошибка (Type mismatch)} end. Строковые переменные описываются описания переменных следующим образом:
в
разделе
<список идентификаторов>: String [<максимальное количество символов>]; Примеры: var S: String [10] ; S1: String[40]; S2, S3, S4: String[255];
Максимальная длина строки не должна превышать 255 символов. Возможно следующее описание строковой переменной S: String; - это описание соответствует строке с максимальной длиной 255 символов (String[255]). Если строковой переменной попытаться присвоить значение с большей, чем максимальная длиной, то ошибки не произойдет, но конец строки будет автоматически отрезан. В программе доступ к отдельным символам строки осу73
ществляется как к элементам массива, посредством указания индекса (от единицы до максимальной длины строки) в квадратных скобках: S[1]:= 'A'; S[4]:= '$'; S[t]:= '!'; и т. п. Нулевой байт содержит длину строки, то есть S[0] будет содержать длину строки S. 19.2 Операции над строками Возможны строковые присваивания: S:= 'Мама мыла раму'; S: = ' ABC ‘ ; S:= S1; При работе со строками допустимо использовать операцию сложения {+). Пример: Sl:= 'ABC’ ; S2: = 'DEF’ ; S:= S1 +S2; После выполнения последнего оператора присваивания переменная S будет иметь значение 'ABCDEF'. Как явствует из примера, сложение строк - это простое их объединение, причем первое слагаемое стоит в новой строке на первом месте, второе - на втором и т. д. Для работы со строками существует набор специфичных процедур и функций. function Length(S: String): Integer ; - функция определяет реальную длину строки (количество символов). Примеры: 1) S : = ' ABCD ' ; t:= length (S) ; После выполнения оператора присваивания целая переменная t будет иметь значение 4 (символа). 2) S:= 'Мама мыла раму'; 74
t:= length (S) ; После выполнения последнего оператора присваивания целая переменная t будет иметь значение 14 (символов). function Pos(Sl, S2: String): Integer ; - функция возвращает номер первой позиции вхождения строки S1 в строку S2. Примеры: 1) S:= 'ABCDEF'; Sl:= 'CDE'; t:= pos(Sl , S); Переменная t будет иметь значение 3. 2) S:= 'АВСАВСАВСАВС ‘ ; S1:= 'BС'; t : = pos(S1, S) ; Переменная t будет иметь значение 2. function Copy(S: String; n, l: integer): String; - функция вырезает и возвращает часть строки S длиной l символов начиная с символа, имеющего номер n. Примеры: 1) S:= 'ABCDEFG’ ; S1:= copy(S, 3, 4); В данном случае функция возвратит значение 'CDEF'. 2) S:= 'мама мыла раму'; S1:= copy(S, 6, 4); Функция возвратит значение 'мыла'. procedure Delete(Var S: String; n, l: integer); — процедура удаляет часть строки S, начиная с позиции n, длиной l символов. Примеры: 1) 75
S:= 'ABCDEFG’; delete (S, 3, 4) ; После вызова процедуры переменная S будет иметь значение 'ABG'. 2) S:= 'мама мыла раму'; delete (S, 6, 5); Переменная S будет иметь значение 'мама раму'. procedure Insert(S1: String; Var S2: String; n: integer); - процедура вставляет строку S1 в строку S2, начиная с позиции n. Примеры: 1) S:= 'ABCDE'; S1:= 'GHIJ’; insert(S1, S, 3); После вызова процедуры Insert переменная S будет иметь значение 'ABGHIJCDE'. 2) S:= 'мама раму'; S1:= 'мыла '; insert(S1, S, 6) ; Переменная S будет иметь значение 'мама мыла раму'. В отличии от массивов, строки можно выводить на экран и вводить с клавиатуры используя операторы Write и Writeln, Read, Readln. Примеры: Write('мама '); Write('мыла ') ; S': = 'раму' ; Writeln (S) ; Read(S); Readln(SI) ; Задача: 76
С клавиатуры вводится строка символов, в которой некоторая подстрока заключена в круглые скобки (например: 'ABC(DEFG)HUK' или 'мама (мыла) раму' ). Требуется отделить фрагмент в скобках и вывести его на экран. program р26; var S: String; nl, n2: integer; begin Writeln('Введите строку'); Readln(S) ; {находим позицию первого вхождения '(' } nl:= pos( '(', S); {- находим позицию первого вхождения ')'} n2:= роз( ')', S); {по двум позициям определяем длину фрагмента - n2 - nl - 1} S: = copy (S, nl + 1, n2 - nl - 1} ; {и вырезаем фрагмент} Writeln('S= ', S); end. Задача: Решить предыдущую задачу при условии, что данный фрагмент заключен между двумя восклицательными знаками (например: 'ABC!DEFG!HIJK' или ‘мама !мыла! раму' ). program p2 7; var S: String; begin {На первый взгляд задача кажется сложней, чем предыдущая: функция Pos позволяет определить только позицию первого вхождения. Но мы решим данную задачу используя процедуру Delete} 77
Writeln('Введите строку'); Readln(S); Delete (S, 1, pos('!’, S) ) ; { удаляем часть строки до восклицательного знака} Delete(S, роs (' !’ , S) , length(S)); {после восклицательного знака} Writeln('S= ', S); end. Задача: С клавиатуры вводится строка. Рассматривая ее как предложение, слова в котором разделяются одним или несколькими пробелами, разбить ее на слова, а слова вывести на экран. Каждое с новой строки. program p28; Const S: String = 'ABC DEFG HIJ KL'; var i: integer; S1: String; begin i: = 1; while i < = length(S) do begin {проходим по возможной цепочке пробелов} While ( S[i] = ' ') and (i <= length(S)) do i : = i + 1 ; S1 : = ‘ ‘ ; {проходим по слову} While ( S[i] <> ' ') and ( i <= length(S)) do begin S1:= S1 + S[i]; i:= i + 1; end; Writeln(S1); end; 78
end. Задача: С клавиатуры вводится строка символов, дополнить ее минимальным количеством символов справа так, чтобы получившаяся строка оказалась палиндромом. (Палиндром - это последовательность символов, читающаяся одинаково и справа на лево, и слева направо. ) program р29; const S: String= 'АВСААС’; var S1, SS: String; i: integer; pol : boolean; begin {Реализуем следующую идею. Будем в цикле проверять, является ли исходная строка палиндромом и отрезать от нее по одному символу слева. Как только получится палиндром, добавим к исходной строке отрезанные от нее символы, предварительно поменяв порядок их следования на обратный} SS := S; S1 : = ' ‘; repeat pol : = true ; for i:= 1 to length (S) div 2 do if S [i] <> S [length (S) - i + 1] then pol:= false; if not pol then begin S1:= S[i] + S1; Delete (S, 1, 1) ; end; Until pol; SS:= SS + S1; 79
Writeln(SS) ; end.
Для преобразования строковых значений в числовые (целые, вещественные) можно воспользоваться процедурой val. procedure Val(S: String; var X: Real[Integer]; var Er : Integer);
Тип параметра Х может быть целым и вещественным, по желанию пользователя. В процессе перевода строки в число может возникнуть ошибка, например, строки 'ABC', 'M1О', '1. 22ddd' и др. в число не переводятся. После вызова процедура Val через параметр Еr возвращает значение ноль, если перевод в число прошел успешно и номер символа, на котором перевод был прерван, в обратном случае. Примеры: {X: Integer} S:= ‘ 423' ; Val (S, x, Er); {после вызова процедуры целая переменная X будет иметь значение 123, а Ег - 0} {R: Real} S:= ‘ 423. 5' ; Val (S, R, Er); {R = 123. 5, Er = 0} {X: Integer} S:= ' 342ABC ' ; Val(S, X, Er); {после вызова процедуры Ег= 4, на четвертом символе произошел сбой} program рЗО; var 80
I, Code: Integer; S: String; begin WriteLn('Введите строку для перевода '); ReadLn(S); Val (ParamStr (1) , I, Code) ; { Если произошла ошибка... } if code <> 0 then Writeln('Ошибка в позиции: ' , Code) else Writeln('Значение: ', I); end.
Процедура Str переводит число (целое или вещественное) в строку. procedure Str(X: Real [Integer]; var S: string);
При переводе числовых значений можно указывать формат перевода, подобно тому, как мы это делали при вызове Write и WriteLn. Примеры: {Т: Integer} Str(T: 6, S) ; {М: Real} Str(M: 4: 2, S); Str(T, S); Str(M, S); program p31; function IntToStr(I: Longint) : String; var S: string [11] ; begin Str(I, S) ; IntToStr:= S; 81
end; begin Writeln(IntToStr(-5322) ) ; end. Задачи для самостоятельного решения 1. Дана строка s. Исключить из нее все лишние пробелы, то есть вместо каждой группы пробелов оставить только один. 2. Дана строка s, состоящая из слов, разделенных между собой одним или несколькими пробелами. Найти номер самого короткого слова. Если таких слов несколько, то указать все их номера. 3. Дана строка s, состоящая из слов, разделенных между собой одним или несколькими пробелами. Определить, сколько раз стоящие рядом два слова начинаются на одну и ту же букву. 4. Дана строка s, состоящая из букв латинского алфавита. Преобразовать данную строку, упорядочив в ней буквы по алфавиту. 5. Дана строка s1. Образовать строку s2, включив в нее символы строки s1, расположенные на нечетных позициях, кроме пробелов и знаков препинания. 20 Генератор случайных чисел В Паскале существует встроенный генератор случайных чисел. Для его инициализации (установки начального значения) используют процедуру Randomize. procedure Randomize;
Для получения случайного числа используют функцию Random, function Random [(Range: Word)]: Integer[Real]; 82
Если диапазон не указан, то возвращается вещественное число в пределах от 0 до 1, иначе в пределах от 0 до range-1. Примеры: program р32; Uses Crt; begin Randomize; repeat TextAttr:= Random(256); Write('!'); until KeyPressed; end. program p33; var S, x: real; begin Randomize; S:= 0; repeat x;= random; Writeln('x=', x:6:4, ' S: = S + x; until S > 3.14 * 100; end.
sin(x)=' , sin(x):6:4);
Задачи для самостоятельного решения 1, Написать программу, по которой компьютер генерирует последовательность из 20 случайных чисел в диапазоне [-100, 100] и подсчитывает сумму тех из них, которые без остатка делятся на 2 и не делятся на 5. 2. Написать программу, проводящую тестирование ученика на знание таблицы умножения. Опрос продолжается до тех пор, пока не будет получено 5 верных ответов подряд. Сколько вопросов было задано? 83
3. Написать игровую программу «Угадай число». Суть игры состоит в следующем: Программа с помощью генератора случайных чисел выбирает число в диапазоне от 0 до 9. Требуется угадать это число за три попытки. После каждой попытки сообщается, больше или меньше названное число задуманного. 21 Записи 21.1 Записи с фиксированными частями Запись в Паскале относится к структурированным типам и в этом смысле отличается от массива только разнотипностью своих компонент, что позволяет использовать ее в качестве набора данных, описывающих некоторый объект. Например, при описании учащихся данного класса школы можно рассмотреть следующие индивидуальные характеристики отдельного ученика: фамилию, имя, месяц и число рождения, вес, рост, номер парты и т. д. Видно, что все данные об ученике разнотипные фамилия, имя - строки, месяц, число, номер парты - целые, вес и рост - вещественные. Таким образом, для хранения информации об учащихся воспользоваться массивом практически невозможно. Но структурированный тип Record предназначен как раз для этого. При описании переменной - записи предварительно определяют идентификатор типа Record в разделе описания типов. type <идентификатор пользовательского типа> = record <список 1 идентификаторов полей записи>: <тип полей>; <список 2 идентификаторов полей записи>: <тип полей>; ……………………………………………………………… <список n идентификаторов полей записи>: <тип полей>; end;
После описания типа можно использовать его иденти84
фикатор при описании любого количества переменных, как мы это делали с идентификаторами стандартных типов. Данный путь не единственный, можно описать переменную типа Record, при этом не описывая типа. var <список идентификаторов переменных>: record <список 1 идентификаторов полей записи>: <тип полей>; <список 2 идентификаторов полей записи>: <тип полей>; ……………………………………………………………… <список n идентификаторов полей записи>: <тип полей>; end;
Запись может быть элементом массива. Такой массив называют массивом записей. Примеры: 1) type child record f, i: String; m, d, n: integer; x, v: Real; end; var a, b, c: child; 2) var a, b, c: record f, i: String; in, d, n: integer; r, v: Real; end; В примерах 1) и 2) переменные a, b, c в результате описания имеют один и тот же тип и структуру. 85
3) type Point = record X, Y: Real; end; Vector = array[0.. 1] of Point; Month = (Jan, Feb, Mar, Apr, May, Jun, Jly, Aug, Sep, Oct, Nov, Dec); Date= record D: 1 . . 31; M: Month; Y: 1900 .. 1999; end; Доступ к конкретному полю записи осуществляется через идентификатор переменной - записи и идентификатор поля, которые разделяются точкой. Примеры: a.f b.r a. v c.m Переменные типа «запись» могут участвовать в операторах присваивания, но никакие операции над ними выполняться не могут. Арифметические или любые другие операции могут выполняться только над отдельными полями записи. Примеры: a . f := 'Иванов’ ; a . i := 'Иван' ; а . т := 12; a . d := 23; a.n:=6; a . r : = 1.78; a . v := 63.5; 86
m: = m + a. r; t:= а . r / a . v;
С целью облегчить выполнение многократных ссылок к полям одной записи используют оператор присоединения: with <переменная-запись> do <оператор>;
Внутри оператора, входящего в оператор присоединения, компоненты записи обозначаются с помощью только имён полей (имя переменной-записи перед ними не указывается). Заголовок операторов может содержать список переменных-записей: with <переменная-запись>{,<переменная-запись>} do <оператор>; Пример (используем запись из приведённого выше примера): 1) with a do begin f:=’Иванов’ ; n:=6; d:=23; r:=1.78; v:=63.5; end; 2) program p41; {программа осуществляет ввод информации об учащихся класса, выводит на экран введенную информацию и отношение роста ученика к его массе} const n:= 30; {количество учеников в классе} type child = record {тип запись-информация об ученике} f, i: String; m, d, n: integer; r, v: Real ; end; 87
TKlass = array[1.. n] of child;{тип массивa - информация об учащихся класса} Var A: TKlass; {экземпляр массива} i, nr: integer; t: real; begin Write('Введите количество учеников – n , 0 < n < 30 ‘ ); Readln(nr); {ввод данных} for i:= 1 to nr do begin Writeln(' N ‘ , i) ; Write('введите фамилию '); Readln(A[i] . f); Write('введите имя '); Readln(A[i] . i); Write('введите месяц рождения '); Readln(A[i] . m); Write('введите день рождения '); Readln (A [i] .d); Write('введите рост '); Readln(A[i].r); Write('введите вес '); Readln(A[i].v); Write('введите номер парты '}; Readln(A[i].n); end; {вывод на экран} for i:= 1 to nr do Writeln(i: 2, '/', A[i].f : 10, '/' , A[i].i : 10, '/' , A[i].d : 2, '/' , A[i].m: 2, '/' , A[i].n : 2, '/ ' , A[i] . r : 4 : 1 , '/' , A[i].v: 4 : 1 , '/' , A[i] . r / A [i] .v : 6: 3) ; end. 21.2 Записи с вариантами 88
Записи не обязательно должны содержать одни и те же компоненты. Поэтому, если необходимо, применяется «запись с вариантами». Запись с вариантами, кроме фиксированной части (которая может отсутствовать) содержит вариантную часть. Варианты могут различаться как числом компонент, так и их типа. Например: type rec = record {описания фиксированных частей} v1, v2 : integer ; {описание вариантной части} case n word of 0: <список полей – описаний переменных>; 1: <список полей – описаний переменных>; ……………………………………………….. k: <список полей – описаний переменных>; end;
Здесь в качестве селектора оператора case используется идентификатор типа. Теперь рассмотрим конкретный пример. Часто возникает необходимость в связи со значением одного поля влиять на тип других полей. Например, в записи о студенте, в зависимости от пола (мужчина, женщина - логический тип), хранить информацию либо о цвете глаз у студентки - строковый тип, либо о размере обуви у студента — вещественный тип. Пример: program p42; type info = record fio: string; case pol : boolean of true: (razmer: Real) ; false: (color: String); end; 89
var X, Y: Info; Begin {Запись Х будет хранить информацию о студенте} X.pol:= true; X.fio:= 'Иванов И. И. ' ; {так как pol=true, то используем поле razmer} X.razmer:= 45.5; {использование поля color в данном случае возможно но вряд ли имеет смысл} {Запись Y будет хранишь информацию о студентке} Y.pol:= true; Y.fio:= 'Петрова М. И. ' ; {так как pol=false, то используем поле color} Y.color:= 'белый’ ; {а в этом случае возможно использование поля razmer, но и это не имеет смысла} {Если попытаться у первой записи вывести на экран поле Color, а у второй razmer, то ошибки не произойдет, но на экране окажутсябессмысленные значия} Writeln(X. color) ; Writeln(Y.razmer) ;
{Это объясняется тем, что для хранения данных полей записи с вариантом используется один и тот же участок в памяти, в первом случае он имеет представление вещественного значения, а во втором строкового. И при обращении к полю color в первой записи компьютер пытается байты вещественного числа представить как строку и наоборот во втором случае . } end. Задачи для самостоятельного решения 1. Написать программу, осуществляющую ввод информации о военнослужащих некоторого войскового подразделения: фамилия, имя, отчество, возраст, рост (от 140 до 210 см) 90
и проверяющую, имеется ли в данном подразделении хотя бы два человека одного роста. 2. Написать программу, осуществляющую ввод информации об учениках школы: имя и фамилия ученика, а также название класса (года обучения и буква), в котором он учится, и проверяющую, есть ли в школе однофамильцы. 3. Написать программу, осуществляющую ввод информации о номерах домашних телефонов сотрудников учреждения (указывается фамилия сотрудника, его инициалы и номер телефона) и выводящую на экран телефон сотрудника по его фамилии и инициалам. 22 Множества
Множество относится к структурным типам данных. Тип «множество» задаёт интервал значений, который является множеством всех подмножеств базового типа. Базовый тип – это перечислимый тип, кроме word, integer, longint. Синтаксис определения типа множества: <имя типа> = set of <базовый тип>; Примеры: Type Tdigit = set of 0 .. 9; Tsimv = set of ‘A’ .. ‘Z’ ; Var digit : Tdigit ; simv : Tsimv;
Если переменная типа «множество» описана как set of 1 .. 3, то она может принимать следующие значения: (1, 2, 3), (1, 2), (1, 3), (2, 3), (1), (2), (3), (). Размер множества равен числу компонент базового типа и может меняться от 0 до 256. Множество может быть пустым. 91
В отличие от массивов, множества содержат переменное количество элементов, для которых порядок следования не важен. Константы множественного типа записываются с помощью квадратных скобок и списка элементов в них. Примеры: Const alpha = [‘A’ .. ‘Z’ , ‘a’ .. ‘z’ ]; empty = [] ; digits = [ 0 .. 9] ; Зарезервированное слово in для определения принадлежности элемента множеству: if ch in alpha then … В этом операторе выполняется проверка принадлежности элемента ch множеству alpha. Часто это гораздо проще, чем многократно сравнивать ch с некоторыми элементами. Множества языка Паскаль обладают свойствами математических множеств. Над ними можно выполнять те же операции. Если S1 и S2 – константы или переменные множественного типа, то S1+ S2 будет их объединением, S1*S2 - пересечением, S1-S2 – разностью. Операции отношений = (равенство), <> (неравенство), <= (является подмножеством), >= (является надмножеством) к множествам применяются, а отношения строго включения < и > - не применяются. Чтобы добавить в множество какой-либо элемент, можно добавить множество, состоящее из единственного элемента. Либо использовать процедуру include(S,a) , где S – множество, в которое добавляем, a – добавляемый элемент. Имеется и обратная процедура – exclude(S , a) - для исключения элемента из множества. Здесь параметры S и a имеют тот же смысл. Пример: program p43; var S: set of ‘A’ .. ‘Z’ ; 92
ch: char; Begin S := [ ]; ch := ‘A’; repeat S := S+[ch] ; Inc(ch); Until ch >= ‘Z’ ; ……………………..
Задачи для самостоятельного решения Написать программу, осуществляющую алгоритм решета Эратосфена для получения всех простых чисел, не превосходящих заданного натурального числа n . Алгоритм состоит в следующем: последовательно, начиная от двух, находим простое число и вычёркивают все числа, кратные найденному числу и не превосходящие n. 23 Процедуры и функции описанные пользователем Начальные сведения о процедурах и функциях даны в разделе "Использование стандартных процедур и функций". Мы познакомились с описанием заголовков, классификацией параметров и т. д. Кроме того, мы уже упоминали о том, что структура процедур и функций во многом повторяет структуру программы. Например, описание процедуры имеет вид: procedure <идентификатор-имя процедуры> ( <список параметров>) ; const <раздед описания констант> type <раздел описания типов> var <раздел описания переменных> label <раздел описания меток> <раздел описания процедур и функций> 93
begin <тело процедуры - операторный_блок> end;
и описание функции : function <идеитификатор - имя процедуры> ( <список параметров>) : <тип значения функции> ; const <раздел описания констант> type <раздел описания типов> var <раздел описания переменных> label <раздел описания меток> <раздел описания процедур и функций> begin <тело процедуры - операторный блок> end;
Функция через свой идентификатор передает значение определенного в заголовке типа. Для этого в теле функции записывается следующий оператор присваивания: <идентификатор-имя функции> : = <выражение соответствующего типа> ; Формальные параметры в процедуре или функции не отличаются от других описанных в ней переменных и могут быть использованы как переменные. Описательные разделы (type, const и др. ) могут отсутствовать, чередоваться, повторяться, если это не нарушает внутренней логики и правильности программы. Надо отметить, что непосредственно в самих процедурах или функциях можно описывать другие процедуры и функции. Возможность описания и использования процедур, вложенных процедур и т. д. заставляет задуматься над использованием переменных, описанных в подпрограммах. Для решения 94
этой проблемы приведем следующие правила локализации переменных. 1. Глобальными переменными называются переменные, описанные в основной программе. Они доступны в любом ее месте и во всех ее подпрограммах. 2. Подпрограмма описывается в некотором описательном разделе назовем его внешним и сама имеет описательный раздел - назовем его внутренним. Локальные переменные - это переменные, описанные в процедурах и функциях, они доступны во всех частях данной подпрограммы и в любых внутренних описательных разделах, но недоступны в тех блоках программы, которые соответствуют внешним описательным разделам. Пример: program p34; var х: Integer ; procedure proс1; var у: Integer; procedure proc2; var z: Integer; begin {тело процедуры ргос2} {доступны переменные: х, у, z} end; begin {тело процедуры prod} {доступны переменные: х, у} end; begin {тело программы} {доступна переменная: х} end. 95
Задача: Написать функцию для возведения целых чисел в натуральную степень. program р35; function fstep(x, n: integer) : Longint; {x - целое число, n - натуральная степень } var i: integer; p: Longint ; begin p:= x; for i:= 2 to n do p:= p * x; fstep:= p; {- значение функции} end; begin Writeln (fstep (3 , 3)); Writeln (fstep (5, 6) ) ; Writeln(fstep(7, 4)); end. Задача: Решить предыдущую задачу используя процедуру. program рЗб; procedure pstep(x, n: integer ; Var p: Longint); {x целое число, n - натуральная степень, р результат выполнения процедуры, передаваемый через параметр-переменную} var i: integer; begin p:=x; for i: = 2 to n do p := p * x; end; var 96
k: Longint; begin pstep(3, 3, k); Writeln(k); pstep (5, 6, k) ,Writeln(k); pstep (7, 4, k); Writeln(k); end. Задача: Используя символы псевдографики, напишите процедуру, выводящую на экран прямоугольное окно с рамкой и заголовком. Параметры окна должны задаваться при вызове процедуры. program p37; uses crt; procedure Wind(x, у, k, h: Integer; s: string); var i: integer; begin { '┌’ , ' ┐’ , '┘' , '└ ' , ' | ‘ , '─' набор символов для рисования рамки} {выводим углы} gotoxy(x, у); write( '┌ ‘); gotoxy(x + k, у); write('┐'); gotoxy(x + k, у + h) ; write('┘'); gotoxy(x, у + h) ; write( '└'); {в цикле выводим символы горизонтальных линий рамки} for i:= 1 to k - 1 do begin gotoxy (x + i, y) ; write ( ' - ' ); gotoxy(x + i, у + h ); write('-'); end; {в цикле выводим символы вертикальных линий рамки} 97
for i := 1 to h-1 do begin gotoxy(x, у + i); write(' | ' ) ; gotoxy(x + k, у + i); wri te(' | ') ; end; {выводим заголовок окна} gotoxy(x + (1 - length (S)) div 2 ); write(S); end; begin clrscr ; Wind(2, 3, 20, 6, ‘Oкно № 1'); end. 24 Параметры-переменные не имеющие типа
Если в качестве формального параметра используется параметр-переменная, для которой тип не указан, то фактический параметр связывается с формальным параметром как с ссылочной переменной. При этом совмещаются физические адреса фактического и формального параметров, а интерпретация данных производится по типу фактического параметра. При использовании формального параметра без типа внутри процедуры необходимо приводить его к ожидаемому типу, т. е. параметр указывается в круглых скобках после имени типа. Связываемый с такими параметрами тип является фиктивным, но он определяет форму доступа к компонентам данных. Примеры: Integer(m) Real(t) Char(g) String(h) В следующей программе описана процедура с параметром без типа, а в теле процедуры параметр сначала интерпретируется и выводится на экран как целое значение, затем как 98
вещественное и строковое. В теле программы процедура вызывается три раза: с параметром переменной целого типа (при этом ее интерпретации в процедуре как вещественного числа и строки - бессмысленны), вещественного и строкового. Пример: program p38; procedure proc (var t) ; begin writeln(integer(t)) ; writeln(real(t)); writeln(string(t)) end; var i integer; r real ; s string; beg in i = 2323; bol(i) ; r = 12. 232; bol (r) ; s = ' ABCD ' ; bol(s) ; end. 25 Процедурные типы Часто возникает необходимость использовать процедуры и функции в качестве формальных параметров. В этом случае среда турбо-Паскаля дает возможность описать специальный процедурный тип. type <идентификатор процедурного типа>: ргоcedure (<список параметров>); 99
<идентификатор процедурного типа>: function (<список параметров> : <тип значения функции>; По существу, запись объявления типа полностью совпадает с записью заголовка процедуры или функции, за исключением того, что в ней опущен идентификатор процедуры (или функции). Примеры: Proc = procedure; SwapProc = procedure(var x, у: integer); StrProc = procedure(s: string); MathFunc = function(x: real): real; DeviceFunc = function(var f: text): integer; MaxPunc = function(a, b: real; f: MathFunc): real; Для работы с переменными процедурного типа, необходимо процедуры и функции, используемые в процедурных присваиваниях и вызовах, описывать как подпрограммы с межсегментной адресацией (дальним вызовом). Для этого после заголовка подпрограммы ставиться служебное слово far. Примеры: program р39; type TProc = procedure; procedure Exec_Proc( р: TProc); begin p; end; procedure p1; far; begin Writeln('процедура №1'); end; procedure p2; far; begin Writeln('процедура №2') ; end; 100
procedure p3; far; begin Writeln ('процедура №3’) ; End; var p: TProc; begin p:= p1; Exec_Proc(p); Exec_Proc(p2); Exec_Proc(p3); end. program p40; type TFunc= function(x: real): real; procedure Func_Tab(f: TFunc; a, b, h; real); var x: real; begin x: = a; While x <= b do begin WriteLn(‘x = ' , x: 6: 4, ' f(x) = ' , f(x): 6: 4) ; x: = x + h ; end; end; function fl(x: real): real; far; begin f1:= sin(x); end; function f2(x: real): real; far; begin 101
f 2:= sin(x) * cos(x); end; function f3 (x: real) : real; far; begin f3:= Ln(x); end; begin Func_Tab (fl, 0, 1, 0. 01); Func_Tab (f2, 0, 6, 0. 1); Func_Tab (f3, 1, 2, 0. 2); end. Задачи для самостоятельного решения 1. Даны действительные числа х, у, z. Получить max( x , y ) + max( x + y , z ) u= . (max( o.5 x + z )) 2 2. Написать логическую функцию, которая возвращает значение true, если среди элементов ее параметра - числовой матрицы – имеется хотя бы два одинаковых элемента, и false в противном случае. 3. Написать функцию, позволяющую найти корень m-й степени ln x m
из х, применив тождество x = e . 4. Написать процедуру, которая меняет местами наибольший и наименьший элементы двумерного числового массива (в предположении, что такие элементы единственны). 5. Написать процедуру транспонирования квадратной матрицы действительных чисел. 6. Написать рекурсивную функцию pow(x, п) от вещественного х (х ≠0) и целого n, которая вычисляет величину х" согласно формуле: m
102
⎧ 1, если т = 0 , ⎪⎪ 1 x m = ⎨ x |m| , если т < 0 , ⎪ x ⋅ x m−1 , если n > 0. ⎪⎩ 26 Файлы Файлы в программе – это каналы, по которым можно выполнять передачу данных. Под файлом понимают набор данных, которые считываются или записываются на внешние устройства. В языке Паскаль имеются три вида файлов: текстовые, типизированные и нетипизированные. Файловая система Паскаля — это часть DOS (дисковой операционной системы). Она имеет древовидную структуру. Корневой каталог, например диска С (логические диски имеют идентификаторы соответствующие буквам английского алфавита - А, В, С, D... ) может содержать подкаталоги, в которых возможно находятся другие подкаталоги и т. д. В корневом каталоге и во всех подкаталогах содержатся файлы. Любой файл характеризуется основным именем и расширением, которые разделяются точкой. Основное имя состоит максимум из восьми символов, а расширение максимум из трех символов. Расширение, как правило, указывает на предназначение файла. Например, pas - текст программы на Паскале, txt - текстовый файл, dat - файл с данными и т. п. Примеры: turbo.ехе met_pas.pas tools.с 103
gol_txt autoexec.bat Каким образом программы, работающие с файлами, знают где находятся необходимые файлы, в каком именно подкаталоге? Для решения этой проблемы при передаче программе или подпрограмме "местонахождения" файла можно воспользоваться понятием - путь к файлу. В Паскале путь к файлу это символьная строка, заключенная в апострофы. Она формируется из имени логического диска и имен системы вложенных подкаталогов, последним из которых является тот, где непосредственно находится файл. В строке имена подкаталогов разделяются символом "\". Пример файловой системы: С: TURBO BIN BGI EXAMPLE UNIT TOOLS ARC SYS MOUSE DRIVERS Примеры: 'C:\TURBO\BGI' ‘D:V’ ‘D:\ARM\BLM\STOP' 'C:\TOOLS\MOUSE’ 26.1 Типизированные файлы Файлом (файловой переменной) в Паскале является объект типа file. Типизированный файл состоит из последовательности однотипных или одинаковых компонент и может содержать различное их число (возможно ни одной). 104
Тип компонент файла практически любой, но он не должен содержать внутри себя других файлов. Определяются файловые переменные следующим образом: <список идентификаторов - имен файловых переменных>: File of <тип компонент файла>; Примеры: var fl: File of Byte; f2: File of Char; f3: File of Integer; f4: File of RJon; При использовании файловой переменной необходимо учитывать следующие ограничения: 1.Файловой переменной не разрешается присваивать другое значение. 2.Если требуется передать файловую переменную в процедуру или функцию, то соответсвующий параметр описывают как параметр переменную. Передача файловой переменной параметром значением приведет к ошибке. Файловая переменная связывается с именем файла на логическом диске. Для этого используют следующую процедуру: procedure Assign (var f: file; S: String); Здесь f- файловая переменная, S — путь и полное имя файла. Примеры: assign(f, 'с:\turbo\bgi\bgi.scr’); assign(fl, 'c:\arm.txt'); assign (f 2, 'd:\pols\data\car.dat'); assign(f3, 'с:\tools\mytext'); В операционной системе есть понятие - текущего подкаталога или буквально — подкаталога, на котором находится указатель операционной системы. Все файловые операции, при которых явно не указывается путь к файлу, происходят именно в текущем подкаталоге. В Паскале для работы с файлами из 105
текущего каталога достаточно при вызове процедуры assign указать только полное имя файла. Чтобы начать работу с файлом, требуется его открыть. Попытка работы с неоткрытым файлом приведет к ошибке. Существует две процедуры для открытия файла. Первая: procedure Reset(var F: File); Она открывает существующий файл, связанный с файловой переменной F. Работая с файлами, приходится опираться на понятие - указателя в файле или файлового указателя. При открытии файла указатель устанавливается перед его первой компонентой. Вторая процедура: procedure Rewrite(var F: File); Она создаёт и открывает новый файл, связанный с файловой переменной F. Если файл с указанным именем уже существует, то старый файл будет стёрт, а на его месте создаётся новый пустой файл. Текущий указатель файла устанавливается в его начало. Примеры: Reset(f); Rewrite(f1); Reset(ff); В программе любой файл (файловая переменная) может быть открыт повторно, т. е. файл, первоначально открытый для чтения, может быть открыт для записи и наоборот. Одна и та же файловая переменная может быть связана с различными физическими файлами. Пример: program p43; var f: file of byte; begin assign(f, 'c:\pols.txt'); rewrite (f) ; {...} 106
close (f); assign(f, 'c:\news.txt'); reset (f) ; {...} close (f) ; end. Часто важно знать находится ли указатель в конце файла (за последней компонентой)? В этом случае можно воспользоваться логической функцией Eof, которая возвращает значение true - если указатель в конце файла и false - если нет. function Eof(var F: file): Boolean; Примеры: t:= Eof(fl); if Eof(ff) then... While not Eof (f2) do.... Указатель файла можно воспринимать как обычный номер текущей компоненты, которые нумеруются начиная с нуля. Таким образом, типизированные файлы являются структурой данных прямого доступа по номеру его компоненты. Чтение из типизированного файла осуществляется процедурой Read. Read(<файловая переменная>, <список переменных-комгюнент>); Примеры: Read(ft, a); Read, (f, a, b, с) ; Переменные считываются из файла и после выполнения каждой операции считывания указатель файла перемещается на следующую компоненту. program р44; var f: file of byte; a, b, c: byte; begin assign(f, 'dospl. pas'); 107
reset (f); read(f, a, b, c); writeln{'a= ', a, ' b= ', b, ' c= ', c); close(f); end. Запись в файл осуществляется с помощью процедуры Write. Write(<файловая переменная>,<список переменных-компонент>); Переменные записываются в файл и после каждой записи указатель в файле перемещается за последнюю компоненту. Для перемещения указателя по файлу можно использовать процедуру Seek. procedure Seek(var f: file; n: Longint); После вызова процедуры указатель файла f перемещается на компоненту с номером n. Например, вызов Seek(f, FileSize(f)); приведет к перемещению указателя в самый конец файла (за последнюю компоненту). Для определения номера компоненты на которой находится указатель файла используют функцию FilePos. function FilePos(var f; file): Longint; Результат выполнения функции - целое число номер текущей компоненты. Если процедура FilePos возвратила значение 0, то указатель стоит в самом начале файла перед первой компонентой. Если функции FilePos и FileSize возвращают одинаковые значения, то указатель находится в самом конце файла. function FileSize(var f: file): Longint; Результат выполнения функции FileSize -длинное целое число размер файла или количество его компонент. Если функция возвращает ноль, то файл пуст. После работы с файлом (файловой переменной) или перед повторным открытием для корректного взаимодействия с операционной системой его необходимо закрыть. С этой целью используют процедуру Close. 108
procedure Close(varf: file); Примеры: program p45; {открываем файл для записи с именем file.int, компонентами которого являются целые числа. Записываем в файл квадраты натуральных чисел до 100} var f: file of Integer; {описываем файловую переменную} i, t: integer; begin Assign(f, 'file.int'); {связываем файловую переменную с именем файла на диске} Rewrite(f); {открываем файл для записи} for i:=1 to 100 do begin t:= sqr(i); Write (f, t); {выводим в файл квадраты целых чисел} end; Close(f); {закрываем файл} end. program p46; {открываем созданный предыдущей программой файл для чтения и выводим его содержание на экран} var f: file of Integer; {описываем файловую переменную} i, t: integer; begin Assign (f, ' file.int' ) ; {связываем файловую переменную с именем файла на диске} Reset(f); {открываем файл для чтения} While not Eof(f) do begin 109
Read(f, t);{читаем очередную компоненту из файла и..} Write(t); {выводим ее на экран} end; Close{f); {закрываем файл} end. Когда приходится хранить в файле информацию о множестве объектов, у каждого из которых много различных признаков (значений различного типа), компонентами типизированных файлов можно делать записи. Задача. Создать программу, записывающую в файл данные о книгах, хранящихся в библиотеке. Для каждой книги требуется следующая информация: номер, фамилия и. о. автора, название книги, количество страниц и год издания. program p47; type Books = record N: Integer; Avtor: String[45]; Nazv: String[70]; Str: Integer; God: Integer; end; var bf: file of Books; r: Books; n, i: integer; begin Writeln('Введите количество книг '); Readln(n); Assign(bf, 'bibl. dat'); Rewrite(bf); for i: = 1 to n do begin r. n:= i; Write('введите фамилию и. о. автора '); 110
Readln(r. avtor); Write('введите название книги '); Readln(r. nazv); Write('введите количество страниц '); Readln(r.str) ; Write('введите год издания '); Readln(r. god) ; Write(bf, r); end; Close(bf); end. Задача. В файле, компонентами которого являются записи - точки плоскости (record х, у: real; end;), выбрать только те, которые находятся в первой и третьей координатных четвертях и записать их в другой файл. Вывести на экран размеры первого и второго файлов. program p48; type points= record х, у: real; end; var fl, f2: file of points; p: points; s1, s2: String; i, nl, n2: Longlnt; begin Write('введите имя исходного файла '); Readln(sl); Write('введите имя результирующего файла '); Readln(s2) ; Assign(fl,s1);{связываем файловые переменные} Assign(f2, s2); {с введенными именами файлов) Reset (fl); {первый файл открываем для чтения} Rewrite(f2); {а второй для записи} 111
While not Eof (fl) do begin Read(fl,p);{читаем очередную запись из первого файла} if ((р. х > 0) and (p. у > 0)) or ((р. х < 0) and (p. у < 0)) then {если точка принадлежит первой или третьей четверти, то выводим ее во второй файл; данное условие можно было записать проще, но менее понятно: if p.x*p. y>0 then... } Write(f2, p); end; nl:=FileSize(f1); {получаем объем первого файла} n2:=FileSize(f2); {... второго} Writeln('nl= ', nl, ' n2 = ', n2); Close(fl); {закрываем файлы} Close (f2) ; end. Если мы открываем файл в программе, то неплохо бы предвидеть различные аварийные ситуации: отсутствует файл с таким именем, не вставлен диск в дисковод и др. При возникновении подобных ситуаций в обычном режиме программа прерывается по ошибке. Но существует возможность избежать этого: директива компилятору {$i-} выключает режим проверки и реакции на ошибки ввода/вывода, а директива {$i+} включает. Однако продолжить выполнение программы в аварийной ситуации недостаточно, важно запрограммировать реакцию на нее - обработать ошибку. Для этого предназначена функция IOResult (результат выполнения операции ввода/вывода). function IOResult: Integer; Функция возвращает целое значение. Если операция ввода/вывода не привела к ошибке, то ее значение ноль, в противном случае функция возвращает номер ошибки. Если ошибка произошла, то все последующие операции ввода/вывода игнорируются до тех пор, пока не будет вызвана функция IOResult. 112
Номера ошибок: Disk read error (ошибка диска при чтению); Disk write error (ошибка диска при записи); File not assigned (файловая переменная не связана с физическим файлом); 103 File not open (файл не открыт); 104 File not open for input (файл не открыт для ввода); 105 File not open for output (файл не открыт для вывода); 106 Invalid numeric format (недопустимый числовой формат). Задача. Проверить существует ли файл с введенным с клавиатуры именем. Если существует, то получить файл с новым именем, в котором порядок следования компонент - байт, изменен на обратный. program p4 9; var fl, f2: file of byte; b: byte ; sl, s2: String; n, i: Longint; begin Write('введите имя исходного файла '); Readln(sl); Write('введите имя результирующего файла '); Readln(s2); Assign(fl,s1);{связываем файловые переменные с } Assign{f2,s2);{введенными именами файлов} {$i-} {- отключаем контроль ошибок в/в} Reset (f1); {первый файл открываем для чтения) if IOResult <> 0 then begin Writeln('Произошла ошибка, файл ', s1,'не существует'); Halt; {прерываем программу} end;
100 101 102
113
{$I+} {- включаем контроль ошибок в/в } Rewrite(f2); { второй файл для записи} n:= FileSize (f1); for i:= n - 1 downto 0 do begin Seek(fl, i); {- устанавливаем указатель на i-ую компоненту первого файла} Read(fl, b); {- читаем эту компоненту} Write(f2, b); {и записываем во второй файл} end; Close(fl); {закрываем файлы} Close (f2) ; End. Задача. Создайте программу, осуществляющую поиск по начальной части фамилии автора и выводящую на экран подходящие записи из файла о книгах (программа р47). program p50; type Books = record N: Integer; Avtor: String[45]; Nazv: String[70]; Str: Integer; God: Integer; end; var bf: file of Books; r: Books; i: integer; s: String; begin Writeln('Введите начальную часть фамилии автора'); Readln(s); Assign(bf, 'bibl.dat’); {Si-} {- отключаем контроль ошибок в/в} 114
Reset(bf); {файл открываем для чтения} if IOResult <> 0 then begin Writeln('Произошла ошибка, файл bibl.dat не существует'); Halt; (прерываем программу} end; {$I+} {- включаем контроль ошибок в/в} While not Eof(bf) do begin Read(bf, r); { Функция Pos(SubS, S: String): Byte возвращает позицию, начиная с которой в строке S располагается подстрока SubS (0 - S не содержит SubS).} if pos(s, r. avtor) = 1 then Writeln(r. n: 3, r. avtor: 12, r. nazv: 14, r. str: 6, r. god: 8) ; end; Close(bf) ; end. Для работы с файловой системой нам могут понадобиться следующие процедуры из модуля System: procedure ChDir(s: string); Процедура изменяет текущий каталог. Здесь s-выражение, содержащее путь к новому каталогу. Пример: program p51; Var S: String; begin Write('Введите путь к каталогу'); Readln(S); {$i-} ChDir(S); if IOResult <> 0 then Writeln('Данный каталог отсутствует'); 115
end.
Следующая процедура procedure GetDir(d: byte; var s: string); - возвращает имя текущего каталога для заданного логического диска. Здесь d – выражение целого типа; s -параметр переменная. (d=0 - означает текущий логический диск, d=1 - диск A, d=2 - диск В и т. д.) Пример: program p52 ; var s: String; begin GetDir (0, s) ; Writeln (' текущий каталог ', s); end. Процедура procedure MkDir(s: string); создает новый каталог. Здесь s - строковое выражение, содержащее путь и имя каталога. Следующая процедура procedure RmDir(s; string); удаляет пустой каталог. Здесь s - путь и имя каталога. Пример: program р53; var S: String; begin S : = ' ABCD ' ; {создаем новый каталог} MkDir(S) ; { . . . и сразу его удаляем} RmDir (S) ; end. Процедура procedure Erase(var j); удаляет файл. Здесь f- переменная файлового типа, связанная 116
с именем внешнего файла с помощью процедуры Assign. Пример: program p54; var F: file; Ch : Char ; S: String; begin Write (' Введите имя удаляемого файла ')/' Readln(S); Assign(F, S) ; {Si-} Reset (F) ; if lOResult <> 0 then Writeln (' файл ' , S, ' отсутствует') else begin Close (F) ; Write ( 'Удалять ', S, '? (y/no) ' ) ; Readln(Ch) ; {Function UpCase(C: char): Char Преобразует латинскую букву в заглавную.} if UpCase(Ch) = 'Y' then Erase (F) ; end; end. Процедура procedure Rename(var f; newname: string); переименовывает файл. Здесь f - переменная файлового типа; newname - строка, содержащая новое имя внешнего файла. Пример: program p55; var f: file; S1, S2: String; begin 117
Write('Введите имя файла'); Readln(S1); Write('Введите новое имя файла '); Readln(S2); Assign(f, S1); Writeln{'Файл ', S1, ' переименован в ', S2); Rename(f, S2} ; end. Задачи для самостоятельного решения 1. Дан файл f, компоненты которого являются целыми числами. Переписать в файл g все компоненты файла f являющиеся точными квадратами. 2. Дан символьный файл f. Получить файл g, образованный из файла f заменой всех его прописных (больших) букв одноименными строчными (малыми). 3. Дан файл содержащий сведения о веществах. Указывается название вещества, его удельный вес и проводимость (проводник, полупроводник, диэлектрик). Вывести на экран названия и удельные веса всех полупроводников. 4. Дан файл, содержащий сведения о различных датах. Каждая дата это число, месяц, год. Найти самую позднюю дату. 5. Написать программу, выводящую на экран все подкаталоги корневого каталога диска С, имена которых состоят не более чем из четырех символов. 26.2 Текстовые файлы В DOS текстовые файлы физически не отличаются от любых других, но в Паскале различия существенны. Элементами текстовых файлов являются символы, объединенные в строки, но текстовый файл это не file of Char или file of String. Каждая строка заканчивается в текстовом файле символом возврата каретки CR=#13 и перевода строки LF=#10. Заканчивается текстовый файл признаком конца файла SUB=#26. Работа с текстовыми файлами очень напоминает работу с консолью. 118
Чтение из текстовых файлов очень похоже на ввод с клавиатуры, а запись в файл напоминает вывод на экран. Указатель в текстовом файле имеет много общего с текстовым курсором. Доступ к элементам текстового файла организуется последовательно. Указатель при считывании очередного элемента файла перемещается к следующему элементу. Для записи в текстовый файл или чтения их него можно использовать процедуры Read, Readln, Write, Writeln. В качестве первого параметра в этих процедурах указывается файловая переменная, например: Read(fp, a, s); Здесь происходит присваивание переменным a и s значений двух очередных элементов их файла, связанного с файловой переменной fp. При выполнении программы на Паскале автоматически открываются два стандартных файла текстового типа. Стандартные файлы имеют имена файловых переменных Input и Output. Стандартная файловая переменная Input представляет собой доступный только для чтения файл для ввода символов с клавиатуры. Вторая файловая переменная Output связана с доступным только для записи стандартным устройством вывода символов и графических элементов на экран монитора. Обращение к файлам Input и Output.происходит автоматически, без каких-либо дополнительных средств с помощью операторов Read, Readln, Write, Writeln. Имя файла в этих процедурах не указывается, если работа ведётся со стандартным файлом. Приведём пример переназначения стандартных файлов (т.е. ввод с клавиатуры и вывод на экран) дисковым файлам. program p73; var N, M: longint; ………. begin assign(Input, 'c:\test.txt'); reset (Input) ; 119
……………. Read(N, M); …………….. close (Input); ………………… assign(Output, 'c:\test_rez.txt'); rewrite(Output) ; ……….. Write(N, M); ………………. close (Output) ; end. Таким образом, эта программа введёт значения переменных N и M из текстового файла test.txt и выведёт результаты работы (значения переменных N и M) в файл test_rez.txt . Файловая переменная для текстового файла описывается следующим образом: <список идентификаторов - имен файловых переменных>:Text; При работе с текстовым файлом, файловую переменную связывают с именем файла, обращаясь к процедуре Assign, а открывают для чтения и записи процедурами Reset и Rewrite соответственно. Процедура Reset открывает текстовый файл только для чтения. Открытый файл закрывается с помощью процедуры Close. В текстовый файл, как и на экран, мы можем выводить значения выражений практически любого типа. При выводе значений операторами Write и WriteLn можно пользоваться стандартными средствами форматирования данных. Примеры: Write(f, x: 6: 4); Writeln(g, 'Т= ' , f 5: 2) ; После вывода оператором Write указатель текстового файла остается в конце выведенной строки, a WriteLn - переходит на начало следующей. Примеры: 120
program p56; {Программа выводит в текстовый файл матрицу n*m} const n= 3; m= 4 ; var Т: array[1.. n, 1.. m] of real; f: Text; {описываем файловую переменную} S: String; i, j: integer; begin Writeln('введите элементы матрицы ', n, '*', m) ; For i:= 1 to n do for j : = 1 to m do begin Write ('T[' , i, *, ' , j, '] = ') ; Readln(T[i, j ] ) ; end; Writeln('введите имя файла'); Readln(s); Assign(f, s); {связываем файловую переменную с именем файла} {$i-} Rewrite(f); {открываем текстовый файл для записи} If IOResult <> 0 then begin Writeln('Ошибка при открытии файла'); Halt; end; {$i+} for i:= 1 to n do begin for j:= 1 to m do Write(f, T[i, j] : 8: 3) ; Writeln(f) ; 121
end; Close (f); end. program p57; {Программа читает матрицу n*m из текстового файла} const n= 3; m= 4; var Т: array[1.. n, 1.. m] of real; f: Text; S: String; i, j: integer; begin Writeln('введите имя файла'); Readln(s); Assign(f, s); {$i-} Reset(f); {открываем текстовый файл для чтения} if lOResult <> 0 then begin Writeln('Ошибка при открытии файла'}; Halt; End; {$i+} for i:= 1 to n do begin for j:= 1 to m do begin Read(f, T[i, j] ) ; Write(T[i, j] : 8: 3); end; Writeln; End; 122
Close(f); end. Задача. Дан текстовый файл, создать второй текстовый файл, уплотнив первый, заменяя в каждой строке серии из двух или более пробелов (32 - ASCII) одним пробелом. program р58; var fl, f2: Text; S: String; Sl, S2: String; {функция параметром получает неуплотненную строку, а возвращает уплотненную} function Uplot(S: String): String; var Z: boolean; begin Z:= false; repeat if pos(' ', S) <> 0 then Delete (S, pos (' ', S) , 1) else Z:= true; Until Z; Uplot:= S; end; begin Write ('введите имя исходного файла '); Readln(sl); Write('введите имя файла с результатом'); Readln(s2); Assign(fl, s1); Assign(f2, s2) ; {$i-} Reset(fl); if lOResult <> 0 then 123
begin Writeln('Произошла ошибка, файл ', s1, 'не существует'); Halt ; end; {$i+} Rewrite(f2); While not Eof(fl) do begin Readln(fl, S); Writeln(f2, Uplot(S)); end; Close(fl) ; Close(f2) ; end. Задача. Даны два текстовых файла. Вывести на экран количество строк в первом и во втором файлах. Если количество строк одинаковое, то вывести на экран те строки второго файла, которые отличаются от соответствующих строк первого. program p59; const Namel = 'filel. txt'; Name2= ‘file2. txt'; var fl, f2: Text; L, K: Integer; S1, S2: String; begin Assign(fl, Namel); Assign(f2, Name2); {$i-} Reset(fl) ; Reset(f2) ; 124
if IQResult <> 0 then begin Writeln('Ошибка при открытии файлов'); Halt; end; {находим количество строк в первом файле} L:= 0; While not Eof(fl) do begin Readln(fl); inc(L); end; {находим количество строк во втором файле} К:= 0; While not Eof(f2) do begin Readln(f2); inc(K); end; Close (fl) ; Close (f 2) ; Reset (fl) ; Reset (f2) ; if К <> L then begin Writeln('файл ', namel, ' - ', L, ' строк' ); Writeln('файл ', namе2, ' - ', К, ' строк’ ); end else begin K:= 0; While not Eof(fl) do begin Readln(fl, SI) ; Readln(f2, S2); Inc(K); 125
if S1 <> S2 then begin Writeln(' (' , K, '): ' , S1) ; Writeln(' (', K, ') : ' , S2) ; end; end; end; Close (fl) ; Close (f2); end. Иногда необходимо открыть текстовый файл для записи, и, не удаляя находящуюся в нем информацию, дописать что-то в его конец. procedure Append(var f: Text); Процедура Append открывает текстовый файл для дозаписи. Если файл с именем, указанным в Assign, не существует, то вызов процедуры Append приведет к ошибке. Пример: program р60; {Программа дописывает в текстовый файл с матрицей новую матрицу - результат транспонирования первой} Const n = 3; m = 4; var Т: array[1..n, 1.. m] of real; ТТ: array[1.. m, 1.. n] of real; f: Text; S: String; i, j: integer; begin Writeln('введите имя файла'); Readln(s); Assign(f, s); 126
{$i-} Reset (f) ; if IOResult <> 0 then begin Writeln('Ошибка при открытии файла'); Halt; end; for i : = 1 to n do for j := 1 to m do begin Read(f, T[i, j] ); TT[j, i] := T[i, j] ; end; Close (f); {закрываем файл} {и повторно открываем его для до записи} Append ( f ) ; {выводим в файл пустую строку разделитель матриц) Writeln(f ) ; for i:= 1 to m do begin {выводим матрицу} for j := 1 to n do Write (f , TT[i, j] : 8: 3) ; Writeln(f) ; end; Close (f) ; end.
Полезны для работы с текстовыми файлами три стандартные логические функции (ниже f - файловая переменная): • EOLN(f) – возвращает значение true, если в файле достигнут конец строки, false – в противном случае; • SEEKEOLN(f) – пропускает пробелы и знаки табуляции до конца строки или до первого значащего символа и возвращает значение true, если достигнут конец строки, false – в противном случае; 127
•
SEEKEOF(f) – пропускает все пробелы, знаки табуляции и маркеры конца строки до конца файла или до первого значащего символа и возвращает значение true, если достигнут конец файла, false – в противном случае.
Задачи для самостоятельного решения 1. Написать программу, формирующую текстовый файл, состоящий из 9 строк, в первой из которых - одна литера «1», во второй - две литеры «2», ..., в девятой - девять литер «9». 2. Дан текстовый файл. Подсчитать в нем количество слов, у которых первый и последний символы совпадают между собой. 3. Дан текстовый файл f. Записать строки файла f в файл g. Порядок слов в строках файла g должен быть обратным по отношению к порядку слов в строках исходного файла. 4. Дан текстовый файл. Написать программу, которая, игнорируя исходное деление этого файла на строки, переформатирует его, разбивая на строки так, чтобы каждая строка оканчивалась точкой, либо содержала ровно 60 символов, если среди них нет точки. 5. С клавиатуры вводится имя текстового файла. Написать программу, выводящую содержимое этого файла на экран или сообщение об отсутствии данного файла в текущем каталоге. 27 Указатели Все структуры, переменные, с которыми мы познакомились ранее, являлись статическими. Для данных объектов память выделяется в самом начале выполнения программы, а их размер не может быть изменен во время её выполнения. Структуры данных, размер которых можно изменять во время выполнения программы, называют динамическими. Например, массив с переменной длинной называют динамическим массивом. Обращение к динамическим объектам производится по 128
их адресу в памяти. Для хранения адреса динамической переменной используется ссылочный тип, а переменная ссылочного типа называется указателем. Значением переменной типа «указатель» является либо nil, либо адрес динамической переменной и занимает она двойное слово (сегмент, смещение). Указатели бывают типизированные и нетипизированные. Для типизированного указателя описание ссылочного типа имеет вид: Type <идентификатор-имя переменной>: ^ <имя базового типа>; Например: Type arr = array[1..100] of real; par = ^arr; pint = ^int; Var a: par; {а – указатель на массив } n: pint ; {n – указатель на целое } b: array[1..5] of ^real; {массив указателей на переменные вещественного типа} Встроенный тип Pointer обозначает нетипизированный указатель, т.е. указатель, который не указывает ни на какой определённый тип. Для порождения динамического объекта служат стандартные процедуры New и GetMem. procedure New( var P:<указатель>); procedure GetMem(var P: Pointer; Size:Word); Процедура New выделяет участок памяти, необходимый для размещения всей переменной указанного типа, а в процедуре GetMem указывается размер необходимого блока памяти. В программе доступ к динамическому объекту происходит через переменную-указатель, после которой ставится символ ^. Пример. program р72 ; 129
var pi : ^Integer;
{описываем указатель на целую переменную} рг: ^ Rеа1; {описываем указатель на вещественную переменную} begin {выделяем память для первой динамической переменной} New (pi) ; {присвоим значение динамической переменной} pi ^: = 25; Writeln (рi^) ; {выводим ее значение на экран} {выделяем память для первой динамической переменной} New(pr) ; pг ^:= 0.5; Writeln (рr^) ; {выводим ее значение на экран} end. Процедурой GetMem удобно пользоваться, когда необходимо создать структурированную динамическую переменную, например, динамический массив. В массиве элементы имеют один тип. Размер, занимаемый n элементами массива, можно получить умножив размер одного элемента на n. А размер элемента возвращает функция SizeOf. function SizeOf(<идентификатop тuпa>): integer; Динамические переменные удаляются из памяти процедурами Dispose или FreeMem. procedure Dispose(var P:<указатель>); Эта процедура освобождает весь участок памяти, выделенный под данную динамическую переменную. procedure FreeMem(var P: Pointer; Size: Word); Данная процедура освобождает участок памяти, размер которого определяет параметр Size. Приведённые процедуры обычно используются 130
парно: процедура Dispose с процедурой New, a. FreeMem - с Get Мет. Пример. program p73; type Element= Integer; DArray= array [1.. 2] of Element; var PA: ^DArray; n, i: integer; begin Write('Введите количество элементов '); Readln(n); {выделяем необходимый участок памяти для динамического массива} GetMem(PA, SizeOf(Element) * n) ; {введем элементы массива с клавиатуры} for i:= 1 to n do Read(PA^[i]); {выведем массив на экран} for i: = 1 to n do Write(PA^[i]: 2); FreeMem(PA, SizeOf(Element) * n); PA:=nil; ………. end. После того, как память освобождена, указателю следует присвоить значение nil во избежание в дальнейшем неприятностей. Для преобразования типов указателей используется нетипизированный указатель типа Pointer, который может иметь значение указателя любого другого типа. Пример: program p76; var р: pointer; pb: ^byte; 131
рс: ^Char; begin new(pb); pbA:= 65; p:= pb; pc:= p; writeln(pc^ ); dispose(pb); end. Задачи для самостоятельного решения 1. Дано целое число k и вещественные матрицы X[1:k,1:k] и Y[1:k+10,1:k+10]. Если наибольший элемент какой либо матрицы больше единицы, то все элементы такой матрицы разделить на этот наибольший элемент, в противном случае оставить матрицу без изменения. 2. Даны целые числа n, m и действительные матрицы а[1:п,1:п], b[1:m,1:m], с[1:п+т,1:п+т]. Преобразовать эти матрицы по следующему правилу, если элементы первой строки образуют неубывающую последовательность и наибольший диагональный элемент (то есть наибольший из элементов, находящихся на главной диагонали), неотрицателен, то упорядочить по неубыванию элементы и всех остальных строк.
132