Основы программирования на языке Паскаль
|>(больше), (не равно), >=(больше или равно), |
|=(D1*(45-2)) |
| S<>'ABC' {Значение переменной S не равно строковой константе 'ABC'} |
| Приведем пример решения еще одной задачи: "Из двух чисел выбрать |
|наибольшее". |
| На первый взгляд решение очевидно, но оно не столь тривиально, как |
|кажется. |
|Program Example; |
|Var A,B,C : Real; {A,B - для хранения аргументов, C - результат} |
|Begin |
|Writeln('Введите два числа'); |
|Readln(A,B); {Вводим аргументы с клавиатуры} |
|If A>B Then C:=A Else C:=B; Если A>B, то результат - A, иначе результат - |
|Writeln(C); {Выводим результат на экран} |
|End. |
| Еще один классический пример: "По заданным коэффициентам решить |
|квадратное уравнение". Эта задача сложнее, поэтому перед тем как писать |
|программу составим алгоритм, записав его в виде блок-схемы. |
|Сначала вводим коэффициенты, затем вычисляем дискриминант. Теперь возникает|
|две возможности: либо отсутствие действительных корней в случае |
|отрицательного дискриминанта, либо эти корни можно все-таки вычислить и |
|вывести на экран в случае неотрицательного дискриминанта (случай равенства |
|дискриминанта нулю входит сюда же, корней - два, только они одинаковые J). |
| При записи алгоритма на языке программирования следует учесть, что в |
|ветви "нет" не одно действие, а три, поэтому следует применить составной |
|оператор. Арифметические выражения не забывайте записывать в соответствии с|
|правилами языка Паскаль. В остальном, эта программа не сложнее предыдущей. |
| |
| |
| |
|Program Sq1; |
|Var A, B, C, D, X1, X2 : Real; |
|Begin |
|Writeln ('Введите коэффициенты квадратного уравнения'); |
|Readln (A,B,C); |
|D:=B*B-4*A*C; |
|If D Do ; |
| Правда, лаконично? По-русски можно прочитать так: "Пока истинно |
|условие, выполнять оператор". Здесь, так же как в формате условного |
|оператора, подразумевается выполнение только одного оператора. Если |
|необходимо выполнить несколько действий, то может быть использован |
|составной оператор. Тогда формат оператора принимает такой вид: |
|While Do |
|Begin |
|; |
|; |
|; |
|. . . |
|End; |
| |
|Цикл "ДО" |
| Этот вид цикла отличается от предыдущего в основном тем, что проверка |
|условия повторения тела цикла находится не перед ним, а после. Поэтому цикл|
|"До" называют циклом "с постусловием", а "Пока" - "с предусловием". |
| Обратите также внимание на то, что новая итерация (повторное |
|выполнение тела цикла) происходит не тогда, когда условие справедливо, а |
|как раз тогда, когда оно ложно. Поэтому цикл и получил свое название |
|(выполнять тело цикла до выполнения соответствующего условия). |
| Интересно, что в случае, когда условие цикла изначально истинно, тело |
|цикла все равно будет выполнено хотя бы один раз. Именно это отличие "до" |
|от "пока" привело к тому, что в программировании они не подменяют друг |
|друга, а используются для решения задач, к которым они более подходят. |
| Формат цикла на языке Pascal: |
|Repeat |
|; |
|; |
|; |
|. . . |
|Until ; |
| Читается так: "Выполнять оператор #1, оператор #2. : до выполнения |
|условия". |
| Здесь не требуется использование составного оператора, потому, что |
|сами слова Repeat и Until являются операторными скобками. |
| |
|Цикл "С параметром". |
| В данном случае параметром будет являться целочисленная переменная, |
|которая будет изменяться на единицу при каждой итерации цикла. Таким |
|образом, задав начальное и конечное значения для такой переменной, можно |
|точно установить количество выполнений тела цикла. Нарисовать блок-схему |
|такой структуры вы сможете сами после некоторых пояснений. |
| Форматов у этого вида цикла предусмотрено два: |
| For := To Do ; |
| For := Downto Do ; |
| Здесь И.П. - имя переменной-параметра, Н.З. - его начальное значение, |
|К.З. - соответственно конечное значение параметра. В качестве начального и |
|конечного значений |
| Читается данная структура так: "Для переменной (далее следует ее имя) |
|от начального значения до конечного выполнять оператор (являющийся телом |
|цикла)". Иногда цикл с параметром даже называют "Для" или "For". В первом |
|случае параметр с каждой итерацией увеличивается на единицу, во втором - |
|уменьшается. |
| Выполняется этот цикл по следующему алгоритму: |
| 1. переменной-параметру присваивается начальное значение; |
| 2. выполняется тело цикла; |
| 3. переменная-параметр автоматически увеличивается на 1 (в первом |
|случае формата); |
| 4. если параметр превышает конечное значение, то происходит выход из |
|цикла, иначе - переход к пункту 2. |
| Примечание: при использовании Downto параметр автоматически |
|уменьшается на 1, а выход из цикла происходит тогда, когда параметр |
|становится меньше конечного значения. |
| Таким образом, в отличие от первых двух видов цикла, этот цикл |
|используется тогда, когда известно необходимое количество выполнений тела |
|цикла. |
| Вообще говоря, цикл "Пока" является универсальным, то есть любая |
|задача, требующая использования цикла, может быть решена с применением этой|
|структуры. Циклы "До" и "С параметром" созданы для удобства |
|программирования. |
| Пример. |
| Найти сумму квадратов всех натуральных чисел от 1 до 100. |
| Решим эту задачу с использованием всех трех видов циклов. |
|I. С использованием цикла "Пока". |
|Program Ex1; |
|Var |
| A : Integer; |
| S : Longint; |
|Begin |
|A:=1; S:=0; |
|While A100; |
|Writeln(S) |
|End. |
|III. С использованием цикла "С параметром". |
|Program Ex3; |
|Var |
| A : Integer; |
| S : Longint; |
|Begin |
|S:=0; |
|For A:=1 To 100 Do S:=S+A*A; |
|Writeln(S) |
|End. |
| Теперь вам известны все основные алгоритмические структуры языка |
|Паскаль. Комбинируя их, возможно запрограммировать решение любой задачи, |
|конечно, если таковое существует. Тем не менее, изучение языка на этом не |
|закачивается, так как для написания хороших программ по утверждению |
|уважаемого Никлауса Вирта (за время моей работы у меня не появилось |
|оснований в этом сомневаться) нужны кроме алгоритмических, еще удобные |
|структуры данных. В рассматриваемом языке таких структур множество, для |
|каждого вида определены свои команды и операции. К их рассмотрению мы и |
|переходим. |
| |
|Строковые операции |
| До сих пор мы с вами рассматривали программы, реализующие алгоритмы |
|обработки числовых данных. Однако хоть ЭВМ изначально и были созданы только|
|для этой цели, по мере развития аппаратной части появилась возможность |
|оцифровывать данные других типов, хранить их в памяти машины, |
|перерабатывать, выводить во внешний по отношению к компьютеру мир. Проще |
|всего можно было так поступить с текстовой информацией. Если не ставить |
|перед машиной задачу "понимания" смысла текста, то задача оцифровки |
|сводится к установлению правил замены символов (литер) при вводе в |
|компьютер на их коды и обратной замены при выводе информации на экран или |
|принтер. Такие правила, конечно же, были составлены. Как водится, сначала |
|их было множество (вспомните разнообразие таблиц кодировки), затем весь мир|
|остановился на ASCII. |
| Все языки программирования высокого уровня имеют средства работы с |
|литерными величинами. Паскаль - не исключение. Как вам уже известно, в |
|стандарте языка описаны два типа переменных для литерных величин. Это - |
|String и Char. Напомню - переменная типа Char может содержать в себе только|
|один единственный символ, тип String предназначен для хранения строковых |
|величин до 255 символов длиною. Кстати, вы знаете не все о типе String. При|
|описании переменной этого типа вы можете сами указать максимальное число |
|символов, которое можно занести в нее. Конечно же, это число не должно |
|превышать 255. Делается это так: |
|Var |
|S : String[30]; |
| Для чего это нужно? |
| Дело в том, что при компиляции для каждой переменной отводится свой |
|участок памяти. Если мы будем выделять для всех переменных типа String по |
|256 байт, то это приведет к тому, что при использовании достаточно большого|
|их количества, памяти может и не хватить? Но если в переменной мы |
|собираемся хранить, например, фамилию пользователя, то тридцати символов |
|(тридцати байт) для этого вполне достаточно. Таким образом, экономится |
|память и увеличивается быстродействие программ. |
| Переменным строкового типа можно присваивать строковые величины |
|(внутри программы они заключаются в апострофы), значения выражений, которые|
|приводят к строковым величинам. Значения можно также вводить с клавиатуры. |
|При этом апострофы не используются. Как вам известно, в числовую переменную|
|нельзя ввести строковую величину. Сделать наоборот - возможно, однако |
|число, находящееся в строковой переменной представляет собой просто |
|последовательность символов (цифр), поэтому в арифметических выражениях |
|участвовать не может. |
| Также, новым для вас явится то, что при использовании строковой |
|переменной, к каждому ее символу можно обратиться отдельно. Необходимо |
|только знать номер нужного символа от начала строки. Его достаточно |
|поставить после имени переменной типа String в квадратных скобках. |
| Пример: S[5] - пятый символ строки S. |
| С отдельным символом строки можно производить все действия, которые |
|можно производить с любой символьной переменной (ввод, присвоение, вывод на|
|экран, участие в выражениях и т.д.). |
| Обратите внимание на то, что нумерация символов в строке начинается с |
|единицы. Внутри квадратных скобок вместо числа может находиться выражение, |
|результатом которого является целое число. Главное чтобы символ с таким |
|номером в строке существовал. Но как же узнать, сколько символов в данный |
|момент находится в строковой переменной? Для этого существует специальная |
|функция, которая возвращает длину строковой переменной в символах. Это |
|функция Length. Ее формат: Length(S) |
| Здесь S - либо строковая величина, либо строковая переменная. |
| Приведенная далее программа выводит на экран длину введенной |
|пользователем строковой величины. |
|Program Str1; |
|Var |
|S : String; |
|Begin |
|Writeln('Введите последовательность символов'); |
|Readln(S); |
|Writeln('Вы ввели строку из ',Length(S), ' символов') |
|End. |
| Другой пример: |
|Решим задачу: "Введенную строку вывести на экран по одному символу в строке|
|экрана". |
|Program Str2; |
|Var |
|S : String; |
|I : Byte; |
|Begin |
|Writeln('Введите строку'); |
|Readln(S); |
|For I:=1 to Length(S) do {организуем цикл, начиная с первого символа} |
|Writeln(S[I]) {строки, до последнего (номер последнего} |
|{совпадает с количеством символов строки S) } |
|End. |
| Какие же еще действия можно выполнять с переменными строкового типа? |
| Две строковые величины можно состыковывать. Эта операция называется |
|конкатенацией и обозначается знаком "+". |
| Например, результатом выполнения следующих команд: |
| R:= 'kadabra'; |
| H:= 'abra'; |
| S:=H+R; |
|в переменной S будет значение 'abrakadabra'. |
| Для конкатенации результат зависит от порядка операндов (в отличие от |
|операции сложения). Следует помнить о том, какой максимальной длины может |
|быть результирующая переменная, так как в случае превышения значением |
|выражения числа, указанного после String в описании переменной, "лишние" |
|символы в переменную не попадут. |
| Строковые величины можно сравнивать между собой. Это относится также и|
|к строковым переменным. Но как же компьютер определяет, какая строка |
|больше: |
|та, которая длиннее? |
|та, которая содержит больше заглавных букв? |
| На самом деле такая проверка проходит довольно сложно: компьютер |
|сравнивает сначала первые символы строк. Большим из двух считается тот, код|
|которого больше (вспомните, что такое код символа). Если равны первые |
|символы, то так же анализируется следующая пара до тех пор, пока не будет |
|найдено различие. Если начало строк совпадает, а одна из них кончается |
|раньше, то вторая автоматически называется большей. |
| Код символа в Паскале можно определить при помощи функции Ord. |
| Ее формат: Ord(C), где С - либо непосредственно указанный символ, либо|
|переменная символьного типа, либо один символ строковой переменной. Вообще,|
|функция Ord имеет более глубокий смысл, но об этом - позже. Есть и обратная|
|функция, которая возвращает символ по известному коду. Это функция Chr(N), |
|где N - выражение, приводящее к целому числу в интервале от 0 до 255 |
|(возможные значения кода символа). Очевидно, что Chr(Ord(C))=C, |
|Ord(Chr(N))=N. |
| Следующая маленькая программа выводит на экран кодовую таблицу: |
|Program Str3; |
|Var |
| I : Byte; |
|Begin |
|For I:=32 to 255 do |
|Write('VV',I:4, '-',Chr(I)) |
|End. |
| Цикл в программе начинается с 32 потому, что символы с кодами от 0 до |
|31 являются управляющими и не имеют соответствующего графического |
|представления. |
| Задача: "Определить, является ли введенная строка "перевертышем". |
|Перевертышем называется такая строка, которая одинаково читается с начала и|
|с конца. Например, "казак" и "потоп" - перевертыши, "канат" - не |
|перевертыш". |
| Поступим следующим образом: из введенной строки сформируем другую |
|строку из символов первой, записанных в обратном порядке, затем сравним |
|первую строку со второй; если они окажутся равны, то ответ положительный, |
|иначе - отрицательный. Естественно, предложенный способ решения не является|
|единственно возможным. |
|Program Str4; |
|Var |
| S,B : String; |
| I : Byte; |
|Begin |
|Writeln('Введите строку'); |
|Readln(S); |
|B:=''; {Переменной B присваиваем значение "пустая строка"} |
|For I:=1 to Length(S) do |
|B:=S[I]+B; {Конкатенация. Символы строки S пристыковываются к} |
|{переменной B слева. Самым левым окажется последний.} |
|If B=S Then Writeln('Перевертыш') Else Writeln('Не перевертыш') |
|End. |
| Число, записанное в строковую переменную, естественно числом не |
|является, но очень часто требуется его все же использовать в качестве |
|числа. Для этого нужно произвести преобразование типа. Перевод строкового |
|представления числа в числовое выполняет в Паскале оператор Val. |
|Его формат: |
|Val(S,X,C); |
| Здесь S - строка, содержащая число, X - числовая переменная, в которую|
|будет помещен результат, С - переменная целочисленного типа, в которую |
|помещается первого встреченного в S отличного от цифры символа. Если после |
|выполнения оператора Val переменная С имеет значение 0, то это означает, |
|что преобразование типа прошло совершенно успешно и в строке нецифровых |
|символов не встретилось. |
| Противоположное действие осуществляет оператор Str. Формат оператора: |
| Str(X,S); |
|X - число (либо арифметическое выражение), S - строковая переменная. |
| В переменную S попадает строковое представление числа X. Это нужно, |
|например, при необходимости выводить на экран числа в графическом режиме |
|(будет изучено позже), так как стандартные процедуры вывода на экран там |
|работают только со строковыми величинами. |
| Для иллюстрации рассмотрим такую задачу: "Найти сумму цифр введенного |
|натурального числа". Используя только числовые переменные, решить ее можно,|
|но предлагаемое здесь решение, по-моему, проще. |
|Program Str5; |
|Var |
|S : String; |
|I,X,A,C : Integer; |
|Begin |
|Writeln('Введите натуральное число'); |
|Readln(S); {Число вводится в строковую переменную} |
|A:=0; |
|For I:=1 To Length(S) Do |
|Begin |
|Val(S[I],X,C); {Цифровой символ превращается в число} |
|A:=A+X {Цифры суммируются} |
|End; |
|Writeln('Сумма цифр равна ',A) |
|End. |
| Теперь рассмотрим еще несколько действий над строками: |
|оператор DELETE(S,I,C) из строковой переменной S удаляет C символов, |
|начиная с I-того; |
|оператор INSERT(SN,S,I) вставляет подстроку SN в строковую переменную S |
|перед символом с номером I; |
|функция COPY(S,I,C) возвращает подстроку строки S из C символов, начиная с |
|символа с номером I; |
|функция Pos(SN,S) возвращает номер символа, с которого в строке S |
|начинается подстрока SN (позицию первого вхождения подстроки в строку). |
|Если такой подстроки нет, то возвращается ноль. |
| Пример их использования: |
|"Во введенной строке заменить все вхождения подстроки 'ABC' на подстроки |
|'KLMNO'". |
|Program Str6; |
|Var |
|S : String; |
|A : Byte; |
|Begin |
|Writeln('Введите строку'); |
|Readln(S); |
|While Pos('ABC',S)<>0 Do |
|Begin |
|A:= Pos('ABC',S); |
|Delete(S,A,3); |
|Insert('KLMNO',S,A) |
|End; |
|Writeln(S) |
|End. |
| |
|Определение типов |
| Как было упомянуто ранее, в изучаемом языке возможно определять новые |
|типы переменных. После определения этот тип становится доступным для |
|описания переменных, также как и стандартные типы. |
| Новый тип перед первым его использованием должен быть описан в |
|соответствующем разделе описаний. Его заголовок - служебное слово Type. |
|Type |
| = ; |
| Есть несколько способов описания. Иногда говорят даже о видах типов |
|(как бы это странно ни звучало). |
| Итак, первым рассмотрим так называемый перечисляемый тип. |
| Перечисляемый тип используется для повышения наглядности программ, |
|позволяя записывать в переменные этого типа названия разнообразных |
|объектов, исследуемых программой. Этот тип представляет собой набор |
|идентификаторов, с которыми могут совпадать значения параметров. |
| Формат описания следующий: = (, ,? ); |
| Далее можно определить любое число переменных уже описанного типа. |
|Обратите внимание на то, что каждый идентификатор может участвовать в |
|описании только одного перечисляемого типа. |
| Этим переменным можно присваивать только значения из списка, |
|определенного при описании типа. Эти значения не являются ни числами, ни |
|строковыми величинами, ни даже величинами логического типа, поэтому они не |
|могут участвовать в арифметических, строковых, логических выражениях, а |
|также не могут быть выведены на экран или принтер. Величины перечисляемого |
|типа можно сравнивать между собой, над их множеством в языке Паскаль |
|определены несколько функций: |
| Ord(X) - порядковый номер значения переменной X в списке |
|идентификаторов. |
| Succ(X) - следующее значение для величины Х. |
| Pred(X) - предыдущее значение данного типа. |
| Обратите внимание на то, что для функции Ord нумерация среди значений |
|идет, начиная от нуля. Для последнего значения нельзя применять функцию |
|Succ, для первого - Pred. |
| Переменные различных перечисляемых типов несовместимы друг с другом. |
| Множество стандартных порядковых типов в языке Паскаль на самом деле |
|определены как перечисляемые. Это типы Char, Integer, другие. Достоинства |
|стандартных порядковых типов лишь в том, что над каждым из них уже |
|определены специфические действия. Например, тип Boolean описан так: |
|Type |
|Boolean = (False, True); |
| Единственное его отличие от перечисляемых типов, определяемых |
|программистом, состоит в том, что значения типа Boolean можно выводить на |
|экран. Можете проверить, Ord(False)=0. |
| Интересно, что переменная перечисляемого типа может быть счетчиком в |
|цикле "с параметром". |
|Пример: |
|Program T1; |
|Type |
| Colors = (Black, Blue, Green, Cyan, Red, Magenta, Brown, Yellow, |
|White); |
|Var |
| C1,C2 : Colors; |
|Begin |
|C1:=Green; |
|C2:=Red; |
|Writeln(Ord(C1), Ord(Succ(C2))) |
|End. |
| Во время выполнения на экране появятся числа "2" и "5", что |
|соответствует номерам значений Green и Magenta. |
| Следующий тип, который можно определить в программе - тип-диапазон. |
| Здесь не нужно перечислять все значения этого типа, потому, что |
|возможными для него являются значения поддиапазона уже определенного до |
|него любого порядкового типа (стандартного или описанного ранее |
|перечисляемого типа). Достаточно лишь указать начальную и конечную величину|
|отрезка порядкового типа. Единственное условие: начальное значение не |
|должно превышать конечное. |
| Формат описания отрезочного типа: |
|Type |
| =..; |
|Примеры: |
|Type |
|Age=0..150; {Целое число в интервале от 0 до 150} |
|Lat='A'.. 'Z'; {Заглавные буквы латинского алфавита} |
|Month=(January, February, March, April, May, June, July, August, September,|
|October, November, December); |
|Spring=March..May; {Весенние месяцы} |
| Есть еще одна возможность определить новый тип, о существовании |
|которой можно было бы и догадаться. |
|Type |
| =; |
|Пример: |
|Type |
| Number=Byte; |
| |
|Массивы |
| До сих пор мы рассматривали переменные, которые имели только одно |
|значение, могли содержать в себе только одну величину определенного типа. |
|Исключением являлись лишь строковые переменные, которые представляют собой |
|совокупность данных символьного типа, но и при этом мы говорили о строке, |
|как об отдельной величине. |
| Вы знаете, что компьютер предназначен в основном для облегчения работы|
|человека с большими информационными объемами. Как же, используя только |
|переменные известных вам типов, сохранить в памяти и обработать данные, |
|содержащие десяток, сотню, тысячу чисел или, к примеру, строк? А ведь такие|
|задачи встречаются в любой области знания. Конечно, можно завести столько |
|переменных, сколько данных, можно даже занести в них значения, но только |
|представьте, какой величины будет текст такой программы, сколько времени |
|потребуется для его составления, как много места для возможных ошибок? |
|Естественно, об этом задумывались и авторы языков программирования. Поэтому|
|во всех существующих языках имеются типы переменных, отвечающие за хранение|
|больших массивов данных. В языке Паскаль они так и называются: "массивы". |
| Массивом будем называть упорядоченную последовательность данных одного|
|типа, объединенных под одним именем. Кстати, под это определение подходит |
|множество объектов из реального мира: словарь (последовательность слов), |
|мультфильм (последовательность картинок) и т. д. Проще всего представить |
|себе массив в виде таблицы, где каждая величина находится в собственной |
|ячейке. Положение ячейки в таблице должно однозначно определяться набором |
|координат (индексов). Самой простой является линейная таблица, в которой |
|для точного указания на элемент данных достаточно знания только одного |
|числа (индекса). Мы с вами пока будем заниматься только линейными |
|массивами, так как более сложные структуры строятся на их основе. |
| Описание типа линейного массива выглядит так: |
| Type =Array [] Of ; |
| В качестве индексов могут выступать переменные любых порядковых типов.|
|При указании диапазона начальный индекс не должен превышать конечный. Тип |
|элементов массива может быть любым (стандартным или описанным ранее). |
| Описать переменную-массив можно и сразу (без предварительного описания|
|типа) в разделе описания переменных: |
| Var : Array [] Of ; |
| Примеры описания массивов: |
|Var |
|S, BB : Array [1..40] Of Real; |
|N : Array ['A'..'Z'] Of Integer; |
|R : Array [-20..20] Of Word; |
|T : Array [1..40] Of Real; |
| Теперь переменные S, BB и T представляют собой массивы из сорока |
|вещественных чисел; массив N имеет индексы символьного типа и целочисленные|
|элементы; массив R может хранить в себе 41 число типа Word. |
| Единственным действием, которое возможно произвести с массивом целиком|
|- присваивание. Для данного примера описания впоследствии допустима |
|следующая запись: |
| S:=BB; |
| Однако, присваивать можно только массивы одинаковых типов. Даже |
|массиву T присвоить массив S нельзя, хотя, казалось бы, их описания |
|совпадают, произведены они в различных записях раздела описания. |
| Никаких других операций с массивами целиком произвести невозможно, но |
|с элементами массивов можно работать точно также, как с простыми |
|переменными соответствующего типа. Обращение к отдельному элементу массива |
|производится при помощи указания имени всего массива и в квадратных скобках|
|- индекса конкретного элемента. Например: |
|R[10] - элемент массива R с индексом 10. |
| Фундаментальное отличие компонента массива от простой переменной |
|состоит в том, что для элемента массива в квадратных скобках может стоять |
|не только непосредственное значение индекса, но и выражение, приводящее к |
|значению индексного типа. Таким образом реализуется косвенная адресация: |
|BB[15] - прямая адресация; |
|BB[K] - косвенная адресация через переменную K, значение которой будет |
|использовано в качестве индекса элемента массива BB. |
| Такая организация работы с такой структурой данных, как массив, |
|позволяет использовать цикл для заполнения, обработки и распечатки его |
|содержимого. |
| Если вы помните, с такой формой организации данных мы встречались, |
|когда изучали строковые переменные. Действительно, переменные типа String |
|очень близки по своим свойствам массивам типа Char. Отличия в следующем: |
|строковые переменные можно было вводить с клавиатуры и распечатывать на |
|экране (с обычным массивом это не проходит); длина строковой переменной |
|была ограничена 255 символами (255 B), а для размера массива критическим |
|объемом информации является 64 KB. |
| Теперь рассмотрим несколько способов заполнения массивов и вывода их |
|содержимого на экран. В основном мы будем пользоваться числовыми типами |
|компонент, но приведенные примеры будут справедливы и для других типов |
|(если они допускают указанные действия). |
|Program M1; |
|Var |
| A : Array [1..20] Of Integer; |
|Begin |
|A[1]:=7; {Заполняем массив значениями (отдельно каждый компонент)} |
|A[2]:=32; |
|A[3]:=-70; |
|.............. {Трудоемкая задача?} |
|A[20]:=56; |
|Writeln(A[1],A[2],A[3], ?,A[20]) |
|End. |
| Как бы ни был примитивен приведенный пример, он все же иллюстрирует |
|возможность непосредственного обращения к каждому элементу массива |
|отдельно. Правда, никакого преимущества массива перед несколькими простыми |
|переменными здесь не видно. Поэтому - другой способ: |
|Program M2; |
|Var |
|A : Array [1..20] Of Integer; |
|I : Integer; |
|Begin |
|For I:=1 To 20 Do {Организуем цикл с параметром I по всем возможным} |
|Readln(A[I]); {значениям индексов и вводим A[I] с клавиатуры } |
|For I:=20 Downto 1 Do {Распечатываем массив в обратном порядке} |
|Write(A[I],'VVV') |
|End. |
| Эта программа вводит с клавиатуры 20 целых чисел, а затем |
|распечатывает их в обратном порядке. Теперь попробуйте написать такую же |
|программу, но без использования структуры массива. Во сколько раз она |
|станет длиннее? Кстати, введение язык Паскаль цикла с параметром было |
|обусловлено во многом необходимостью обработки информационных |
|последовательностей, т. е. массивов. |
| Следующая программа заполняет массив значениям квадратов индексов |
|элементов: |
|Program M3; |
|Const |
|N=50; {Константа N будет содержать количество элементов массива} |
|Var |
|A : Array [1..N] Of Integer; |
|I : Integer; |
|Begin |
|For I:=1 To N Do |
|A[I]:=I*I |
|For I:=1 To N Do |
|Write(A[I],'VVV') |
|End. |
| В дальнейшем для учебных целей мы будем использовать массивы, заданные|
|с помощью генератора случайных чисел. В языке Паскаль случайные числа |
|формирует функция Random. Числа получаются дробными, равномерно |
|расположенными в интервале от 0 до 1. Выражение, дающее целое случайное |
|число в интервале [-50,50] будет выглядеть так: |
|Trunc(Random*101)-50 |
|Зададим и распечатаем случайный массив из сорока целых чисел: |
|Program M4; |
|Const |
|N=40; {Константа N будет содержать количество элементов массива} |
|Var |
|A : Array [1..N] Of Integer; |
|I : Integer; |
|Begin |
|For I:=1 To N Do |
|Begin |
|A[I]:= Trunc(Random*101)-50 |
|Write(A[I],'VVV') |
|End |
|End. |
| С обработкой линейных массивов связано множество задач. Их мы |
|рассмотрим на практических занятиях. |
| |
|Двумерные и многомерные массивы |
|Представьте себе таблицу, состоящую из нескольких строк. Каждая строка |
|состоит из нескольких ячеек. Тогда для точного определения положения ячейки|
|нам потребуется знать не одно число (как в случае таблицы линейной), а два:|
|номер строки и номер столбца. Структура данных в языке Паскаль для хранения|
|такой таблицы называется двумерным массивом. Описать такой массив можно |
|двумя способами: |
|I. |
|Var |
| A : Array [1..20] Of Array [1..30] Of Integer; |
|II. |
|Var |
| A : Array [1..20,1..30] Of Integer; |
|В обоих случаях описан двумерный массив, соответствующий таблице, состоящей|
|из 20 строк и 30 столбцов. Приведенные описания совершенно равноправны. |
|Отдельный элемент двумерного массива адресуется, естественно, двумя |
|индексами. Например, ячейка, находящаяся в 5-й строке и 6-м столбце будет |
|называться A[5][6] или A[5,6]. |
|Для иллюстрации способов работы с двумерными массивами решим задачу: |
|"Задать и распечатать массив 10X10, состоящий из целых случайных чисел в |
|интервале [1,100]. Найти сумму элементов, лежащих выше главной диагонали." |
|При отсчете, начиная с левого верхнего угла таблицы, главной будем считать |
|диагональ из левого верхнего угла таблицы в правый нижний. При этом |
|получается, что элементы, лежащие на главной диагонали будут иметь |
|одинаковые индексы, а для элементов выше главной диагонали номер столбца |
|будет всегда превышать номер строки. Договоримся также сначала указывать |
|номер строки, а затем - номер столбца. |
|Program M5; |
|Var |
|A : Array[1..10,1..10] Of Integer; |
|I, K : Byte; |
|S : Integer; |
|Begin |
|S:=0; |
|For I:=1 To 10 Do |
|Begin |
|For K:=1 To 10 Do |
|Begin |
|A[I,K]:=Trunc(Random*100)+1; |
|Write(A[I,K]:6); |
|If K>I Then S:=S+A[I,K] |
|End; |
|Writeln |
|End; |
|Writeln('Сумма элементов выше гл. диагонали равнаV',S) |
|End. |
|Если модель данных в какой-либо задаче не может свестись к линейной или |
|плоской таблице, то могут использоваться массивы произвольной размерности. |
|N-мерный массив характеризуется N индексами. Формат описания такого типа |
|данных: |
|Type |
|=Array[,,... |
|] Of ; |
|Отдельный элемент именуется так: |
| [,,...,] |
| |
|Процедуры и функции |
|При решении сложных объемных задач часто целесообразно разбивать их на |
|более простые. Метод последовательной детализации позволяет составить |
|алгоритм из действий, которые, не являясь простыми, сами представляют собой|
|достаточно самостоятельные алгоритмы. В этом случае говорят о |
|вспомогательных алгоритмах или подпрограммах. Использование подпрограмм |
|позволяет сделать основную программу более наглядной, понятной, а в случае,|
|когда одна и та же последовательность команд встречается в программе |
|несколько раз, даже более короткой и эффективной. |
|В языке Паскаль существует два вида подпрограмм: процедуры и функции, |
|определяемые программистом. Процедурой в Паскале называется именованная |
|последовательность инструкций, реализующая некоторое действие. Функция |
|отличается от процедуры тем, что она должна обязательно выработать значение|
|определенного типа. |
|Процедуры и функции, используемые в программе, должны быть соответствующим |
|образом описаны до первого их упоминания. Вызов процедуры или функции |
|производится по их имени. |
|Подпрограммы в языке Паскаль могут иметь параметры (значения, передаваемые |
|в процедуру или функцию в качестве аргументов). При описании указываются |
|так называемые формальные параметры (имена, под которыми будут фигурировать|
|передаваемые данные внутри подпрограммы) и их типы. При вызове подпрограммы|
|вместе с ее именем должны быть заданы все необходимые параметры в том |
|порядке, в котором они находятся в описании. Значения, указываемые при |
|вызове подпрограммы, называются фактическими параметрами. |
|Формат описания процедуры: |
|Procedure (:; |
|< Имя форм. параметра 2>:?); |
| |
|Begin |
| |
|End; |
|Раздел описаний может иметь такие же подразделы, как и раздел описаний |
|основной программы (описание процедур и функций - в том числе). Однако все |
|описанные здесь объекты "видимы" лишь в этой процедуре. Они здесь локальны |
|также, как и имена формальных параметров. Объекты, описанные ранее в |
|разделе описаний основной программы и не переопределенные в процедуре, |
|называются глобальными для этой подпрограммы и доступны для использования. |
|Легко заметить схожесть структуры программы целиком и любой из ее процедур.|
|Действительно, ведь и процедура и основная программа реализуют некий |
|алгоритм, просто процедура не дает решения всей задачи. Отличие в заголовке|
|и в знаке после End. |
|Формат описания функции: |
|Function (:; |
|< Имя форм. параметра 2>:?) : ; |
| |
|Begin |
| |
|End; |
|В теле функции обязательно должна быть хотя бы команда присвоения такого |
|вида: :=; |
|Указанное выражение должно приводить к значению того же типа, что и тип |
|результата функции, описанный выше. |
|Вызов процедуры представляет в программе самостоятельную инструкцию: |
|(, < Фактический параметр 2>?); |
|Типы фактических параметров должны быть такими же, что и у соответсвующих |
|им формальных. |
|Вызов функции должен входить в выражение. При вычислении значения такого |
|выражения функция будет вызвана, действия, находящиеся в ее теле, будут |
|выполнены, в выражение будет подставлено значение результата функции. |
|Приведем простейший пример использования подпрограммы. |
|Задача: "Найти максимальное из трех введенных чисел". Для решения |
|воспользуемся описанием функции, принимающей значение максимального из двух|
|чисел, которые передаются в нее в виде параметров. |
|Program Fn; |
|Var |
|A,B,C :Real; |
|Function Max(A,B:Real):Real; {Описываем функцию Max с формальными} |
|Begin {параметрами A и B, которая принимает } |
|If A>B Then Max:=A {значение максимального из них } |
|Else Max:=B {Здесь A и B - локальные переменные } |
|End; |
|Begin |
|Writeln('Введите три числа'); |
|Readln(A,B,C); |
|Writeln('Максимальным из всех является ', Max(Max(A,B),C)) |
|End. |
|Обратите внимание на краткость тела основной программы и на прозрачность |
|действий внутри функции. Формальные параметры A и B, используемые в |
|подпрограмме, не имеют никакого отношения переменным A и B, описанным в |
|основной программе. |
|Существует два способа передачи фактических параметров в подпрограмму: по |
|значению и по ссылке. В первом случае значение переменной-фактического |
|параметра при вызове подпрограммы присваивается локальной переменной, |
|являющейся формальным параметром подпрограммы. Что бы потом ни происходило |
|с локальной переменной, это никак не отразится на соответствующей |
|глобальной. Для одних задач это благо, но иногда требуется произвести в |
|подпрограмме действия над самими переменными, указанными в качестве |
|фактических параметров. На помощь приходит второй способ. Происходит |
|следующее: при обращении к подпрограмме не происходит формирования |
|локальной переменной-формального параметра. Просто на время выполнения |
|подпрограммы имя этой локальной переменной будет указывать на ту же область|
|памяти, что и имя соответствующей глобальной переменной. Если в этом случае|
|изменить локальную переменную, изменятся данные и в глобальной. |
|Передача параметров по ссылке отличается тем, что при описании подпрограммы|
|перед именем переменной-формального параметра ставится служебное слово Var.|
|Теперь использование в качестве фактических параметров выражений или |
|непосредственных значений уже не допускается - они должны быть именами |
|переменных. |
|Еще один классический пример. Задача: "Расположить в порядке неубывания три|
|целых числа". |
|Program Pr; |
|Var |
| S1,S2,S3 :Integer; |
|Procedure Swap(Var A,B: Integer);{Процедура Swap с параметрами-переменными}|
| |
|Var C : Integer; {C - независимая локальная переменная} |
|Begin |
| C:=A; A:=B; B:=C {Меняем местами содержимое A и B} |
|End; |
|Begin |
|Writeln('Введите три числа'); |
|Readln(S1,S2,S3); |
|If S1>S2 Then Swap(S1,S2); |
|If S2>S3 Then Swap(S2,S3); |
|If S1>S2 Then Swap(S1,S2); |
|Writeln('Числа в порядке неубывания:V',S1,S2,S3) |
|End. |
| |
|Работа с файлами |
|Тип-файл представляет собой последовательность компонент одного типа, |
|расположенных на внешнем устройстве (например, на диске). Элементы могут |
|быть любого типа, за исключением самого типа-файла. Число элементов в файле|
|при описании не объявляется. Работа с физическими файлами происходит через |
|так называемые файловые переменные. |
|Для задания типа-файла следует использовать зарезервированные слова File и |
|Of, после чего указать тип компонент файла. |
|Пример: |
|Type |
|N = File Of Integer; {Тип-файл целых чисел} |
|C = File Of Char; {Тип-файл символов} |
|Есть заранее определенный в Паскале тип файла с именем Text. Файлы этого |
|типа называют текстовыми. |
|Введя файловый тип, можно определить и переменные файлового типа: |
|Var |
|F1 : N; |
|F2 : C; |
|F3 : Text; |
|Тип-файл можно описать и непосредственно при введении файловых переменных: |
|Var |
| Z : File Of Word; |
|Файловые переменные имеют специфическое применение. Над ними нельзя |
|выполнять никаких операций (присваивать значение, сравнивать и т.д.). Их |
|можно использовать лишь для выполнения операций с файлами (чтение, запись и|
|т.д.). |
|Элементы файла считаются расположенными последовательно, то есть так же, |
|как элементы линейного массива. Отличие же состоит в том, что, во-первых, |
|размеры файла могут меняться, во-вторых, способ обращения к элементам |
|совсем другой: невозможно обратиться к произвольному элементу файла; |
|элементы его просматриваются только подряд от начала к концу, при этом в |
|каждый момент времени доступен только один элемент. Можно представить себе,|
|что для каждого файла существует указатель, показывающий в данный момент на|
|определенный компонент файла. После проведения операции чтения или записи |
|указатель автоматически передвигается на следующий компонент. |
|Перед тем, как осуществлять ввод-вывод, файловая переменная должна быть |
|связана с конкретным внешним файлом при помощи процедуры Assign. |
|Формат: |
| Assign(,); |
|Имя файла задается либо строковой константой, либо через переменную типа |
|Sting. Имя файла должно соответствовать правилам работающей в данный момент|
|операционной системы. Если строка имени пустая, то связь файловой |
|переменной осуществляется со стандартным устройством ввода-вывода (как |
|правило - с консолью). |
|После этого файл должен быть открыт одной из процедур: |
|Reset(); |
|Открывается существующий файл для чтения, указатель текущей компоненты |
|файла настраивается на начало файла. Если физического файла, |
|соответствующего файловой переменной не существует, то возникает ситуация |
|ошибки ввода-вывода. |
|Rewrite(); |
|Открывается новый пустой файл для записи, ему присваивается имя, заданное |
|процедурой Assign. Если файл с таким именем уже существует, то он |
|уничтожается. |
|После работы с файлом он, как правило, должен быть закрыт процедурой Close.|
| |
|Close(); |
|Это требование обязательно должно соблюдаться для файла, в который |
|производилась запись. |
|Теперь рассмотрим непосредственную организацию чтения и записи. |
|Для ввода информации из файла, открытого для чтения, используется уже |
|знакомый вам оператор Read. Правда, в его формате и использовании вы |
|заметите некоторые изменения: |
|Read(, ); |
|Происходит считывание данных из файла в переменные, имена которых указаны в|
|списке ввода. Переменные должны быть того же типа, что и компоненты файла. |
| |
|Вывод информации производит, как можно догадаться оператор Write(, ); |
|Данные из списка вывода заносятся в файл, открытый для записи. |
|Для текстовых файлов используются также операторы Readln и Writeln с |
|соответствующими дополнениями, относящимися к файловому вводу-выводу. |
|Любопытно, что вывод данных на монитор и ввод с клавиатуры в языке Паскаль |
|тоже являются действиями с файлами. Они даже имеют свои предопределенные |
|файловые переменные текстового типа: Output и Input соответственно. |
|Переменная Output всегда открыта для записи, Input - для чтения. Если не |
|указывать файловые переменные в операторах ввода-вывода (придем к формату, |
|рассмотренному в теме "Операторы ввода-вывода"), то в случае записи по |
|умолчанию выбирается файл Output, в случае чтения - Input. |
|Как вы знаете, любой файл конечен и продолжать чтение из него информации |
|можно лишь до определенного предела. Как этот предел установить? Проверить,|
|окончен ли файл, можно вызовом стандартной логической функции Eof() |
|Она вырабатывает значение True, если файл окончен, и False - в противном |
|случае. |
|Решим следующую задачу: "Написать программу, которая вводит с клавиатуры |
|список фамилий учащихся, а затем распечатывает его, кроме тех учащихся, у |
|которых фамилия начинается с буквы 'Ш'". |
|Так как заранее количество данных не известно, то для их хранения |
|используем файл. Тип элементов - строковый. |
|Program L; |
|Var |
|I,N : Integer; |
|F : File Of String; |
|S : String; |
|Begin |
|Assign(F,'Spis.lst'); {Связываем переменную F с файлом Spis.lst} |
|Writeln('Введите количество учащихся'); |
|Readln(N); {Вводим количество учащихся} |
|Rewrite(F); {Создаем файл для записи в него данных} |
|For I:=1 To N Do {Для всех учащихся} |
|Begin |
|Writeln('Введите фамилию'); |
|Readln(S); |
|Write(F,S) |
|End; |
|Close(F); |
|Reset(F); |
|Writeln; Writeln('Список учащихся:'); |
|While Not(Eof(F)) Do |
|Begin |
|Read(F,S); |
|If S[1]<>'Ш' Then |
|Writeln(S) |
|End; |
|Close(F) |
|End. |
Страницы: 1, 2
|