
- •Учебно-методическое пособие по изучению теоретического материала и самостоятельному выполнению лабораторных заданий
- •1. Введение
- •2. Общие методические рекомендации по изучению курса
- •3. Методические рекомендации по изучению теоретического материала курса
- •3.1 Содержание разделов дисциплины
- •Тема 1. «Структура программы на языке «Паскаль». Основные типы данных. Правила записи имён в языке Паскаль. Линейные вычислительные процессы.»
- •Тема 2. «Базовые конструкции языка «Паскаль». Ветвящиеся вычислительные процессы.»
- •Тема 3. «Одномерные и многомерные массивы. Циклические вычислительные процессы.»
- •Тема 4. «Обработка символьной информации»
- •Тема 5. «Процедуры в языке Паскаль»
- •Тема 6. «Функции в языке Паскаль»
- •Тема 7. Рекурсивные функции. Явная и неявная рекурсия.
- •Тема 8. Многомодульные приложения (дополнительная тема).
- •4. Методические рекомендации по выполнению лабораторных работ
- •4.1 Лабораторная работа №1 «Структура программ на языке Паскаль, основные типы данных, основные операции, линейные вычислительные процессы»
- •Варианты заданий на лабораторную работу № 1.
- •Var a,b,c,r : real;
- •Var a,b,c,r : real;
- •Var a,b,c,r : real;
- •4.2 Лабораторная работа №2 «Условные операторы. Правила записи условий простые и сложные условия, ветвящиеся вычислительные процессы»
- •Варианты заданий на лабораторную работу № 2.
- •Var a,b,X : real;
- •Var a,b,X : real;
- •Var a,b,X : real;
- •Var a,b,X : real;
- •Var a,b,X : real;
- •4.3 Лабораторная работа №3 «Массивы и циклы в языке Паскаль»
- •Варианты заданий на лабораторную работу № 3.
- •4.4 Лабораторная работа №4 «Обработка символьной и строковой информации в языке Паскаль»
- •Варианты заданий на лабораторную работу № 4.
- •4.5 Лабораторная работа №5 «Процедуры в языке Паскаль»
- •Варианты заданий на лабораторную работу № 5.
- •Var a,b : integer; // Числа, вводимые пользователем
- •I : integer; // Счётчик цикла
- •Var a,b : integer; // Числа, вводимые пользователем
- •I : integer; // Счётчик цикла
- •Var a,b : integer; // Числа, вводимые пользователем
- •I : integer; // Счётчик цикла
- •Var X : integer;
- •Var I : integer; // Счётчик цикла
- •4.6 Лабораторная работа №6 «Функции в языке Паскаль»
- •Варианты заданий на лабораторную работу № 6.
- •Пример решения типовой задачи лабораторной работы № 6.
- •Var I : integer; // Счётчик цикла
- •Var X : integer;
- •Var X : integer;
- •В результате внесённых изменений наша программа примет следующий вид:
- •Var X : integer;
- •Результат работы этой программы представлен на следующем рисунке:
- •Var X : integer;
- •4.7 Лабораторная работа №7 «Рекурсия и рекурсивные функции в языке Паскаль»
- •Варианты заданий на лабораторную работу № 7.
- •Var x1, x2, Xn : real; // Вычисляемые числа
- •Var x1, x2, Xn : real; // Вычисляемые числа
- •Var x1, x2, Xn : real; // Вычисляемые числа
- •Var r : real ; // Граница вычислений, вводимая пользователем
- •Var x1, x2, Xn : real ; // Вычисляемые числа
- •I : integer; // Число итераций цикла
- •Var r : real ; // Граница вычислений, вводимая пользователем
- •Var Xn : real; // Вычисляемые числа
- •Var Xn : real; // Вычисляемые числа
- •5. Вопросы для подготовки к зачёту
- •6. Список рекомендуемой литературы
- •7. Программное обеспечение и Интернет-ресурсы
Var a,b : integer; // Числа, вводимые пользователем
Rez : integer; // Результат вычислений
I : integer; // Счётчик цикла
Этап второй. Разработка простой программы прямым кодом, без использования процедур.
Как осуществляется ввод данных в языке «Паскаль» мы уже знаем. Потому не будем отвлекаться на это.
По условию задачи нам необходимо подсчитать общую сумму квадратов всех чисел, расположенных в диапазоне, указанном пользователем. Следовательно, нам необходимо организовать циклический вычислительный процесс, в границах, заданных пользователем, который на каждом шаге вычисляет квадрат текущего числа и добавляет его к общей сумме.
Всё легко и просто. Всё, что необходимо для этого – мы уже знаем!
Var a,b : integer; // Числа, вводимые пользователем
Rez : integer; // Результат вычислений
I : integer; // Счётчик цикла
Begin
// Приветствие.
Writeln('Данная программа предназначена');
Writeln('для вычисления суммы квадратов всех чисел,');
Writeln('расположенных в указанном Вами диапазоне.');
Writeln;
// Ввод диапазона вычислений
Write('Введите начало диапазона. a='); Readln(a);
Write('Введите окончание диапазона. b='); Readln(b);
// Вычисление суммы квадратов.
Rez:=0;
for i:=a to b do Rez:=Rez + i*i;
// Печать результата вычислений
Writeln('Сумма квадратов всех целых чисел в указанном
диапазоне равна - ', Rez);
End.
Результат работы нашей программы приведён на следующем рисунке:
Этап третий. Оформление смысловых частей программы в процедуры. С использованием глобальных переменных.
На этом этапе переоформим нашу программу так, что бы каждая из частей нашей программы была представлена в виде независимой процедуры.
Для начала выделим в отдельную процедуру вычисление.
Var a,b : integer; // Числа, вводимые пользователем
Rez : integer; // Результат вычислений
I : integer; // Счётчик цикла
// Процедура вычисления суммы квадратов, использующая глобальные переменные.
Procedure Calc;
Begin
Rez:=0;
for i:=a to b do Rez:=Rez + i*i;
End;
// Главная часть программы
Begin
// Приветствие.
Writeln('Данная программа предназначена');
Writeln('для вычисления суммы квадратов всех чисел,');
Writeln('расположенных в указанном Вами диапазоне.');
Writeln;
// ВВод диапазона вычислений
Write('Введите начало диапазона. a='); Readln(a);
Write('Введите окончание диапазона. b='); Readln(b);
// Вызов процедуры вычисления.
Calc;
// Печать результата вычислений
Writeln('Сумма квадратов всех целых чисел в указанном диапазоне равна - ', Rez);
End.
Теперь переоформим в процедуры приветствие, ввод исходных данных и вывод результатов вычисления. По-прежнему используя глобальны переменные.
Var a,b : integer; // Числа, вводимые пользователем
Rez : integer; // Результат вычислений
i : integer; // Счётчик цикла
//-------------------------------------------------------
// Процедура приветствия.
Procedure Hello;
Begin
Writeln('Данная программа предназначена');
Writeln('для вычисления суммы квадратов всех чисел,');
Writeln('расположенных в указанном Вами диапазоне.');
Writeln;
End;
//-------------------------------------------------------
// Процедура ввода исходных данных, использующая
// глобальные переменные.
Procedure Vvod;
Begin Write('Введите начало диапазона. a='); Readln(a);
Write('Введите окончание диапазона. b='); Readln(b);
End;
//-------------------------------------------------------
// Процедура вычисления суммы квадратов, использующая
// глобальные переменные.
Procedure Calc;
Begin Rez:=0;
for i:=a to b do Rez:=Rez + i*i;
End;
//-------------------------------------------------------
// Процедура вывода результатов вычисления, использующая
// глобальные переменные.
Procedure Print;
Begin
Writeln('Сумма квадратов всех целых чисел в указанном
диапазоне равна - ', Rez);
End;
//------------------------------------------------------
// Главная часть программы
Begin Hello; // Вызов процедуры приветствия.
Vvod; // Вызов процедуры ввода исходных данных.
Calc; // Вызов процедуры вычисления.
Print; // Вызов процедуры печати результата вычислений.
End.
Этап четвёртый. Замена использования глобальных переменных на механизм передачи параметров.
При написании больших программ, в целях облегчения работы с различными частями этих программ и повышения «читаемости» программного кода, программы разбивают на отдельные модули (библиотеки). При этом очень часто возникает такая ситуация, когда переменные, объявленные в одном из модулей, недоступны (не видны) в других модулях. Объявление же всех переменных до включения в программу всех модулей (т.е. придание переменным статуса глобальных для всего многомодульного программного проекта) также нецелесообразно. Почему нецелесообразно? Потому, что написанный однажды модуль может быть использован в будущем при написании других программ, но при этом, в новых программах будут отсутствовать те глобальные переменные, которые использовались в прошлом программном проекте. Следовательно, модуль окажется неработоспособным.
В связи с указанными выше причинами, в языке Паскаль, был предусмотрен механизм прямой передачи данных в процедуры. Для этого необходимо всего лишь указать что именно должно передаваться в процедуры, и что мы хотим получить обратно.
Рассмотрим процедуры нашей программы.
Процедура приветствия «Procedure Hello» – не использует ни каких данных и ничего не возвращает обратно. Следовательно изменение данной процедуры не требуется.
Процедура ввода исходных данных «Procedure Vvod» – должна вернуть нам два числа, введённых пользователем. Следовательно, нам необходимо передать этой процедуре два параметра, передаваемых как переменные:
Procedure Vvod(var p1, p2 : integer)
Процедура вычисления «Procedure Calc» – использует при вычислении два исходных числа, счётчик цикла и должна вернуть нам результат вычислений. Следовательно, нам необходимо передать этой процедуре: два параметра, являющихся исходными значениями введённых пользователем данных, и один параметр, передаваемый как переменная, для получения результата вычислений. Счётчик цикла передавать нет необходимости. Достаточно объявить его внутри процедуры. Как локальную переменную.
Procedure Calc(x,y : integer; VAR z : integer)
Процедура вывода результата на экроан «Procedure Print» – использует при своей работе одно значение, являющееся результатом вычислений. Следовательно, мы должны передать этой процедуре один параметр, являющийся этим значением:
Procedure Print(R : integer)
Замечание1. Обратите внимание на то, что имена основных (глобальных) переменных и имена параметров процедур не совпадают. Для каждой процедуры параметры имеют локальное значение и, следовательно, могут иметь любые имена. Реальные же значения они получат в момент вызова процедур, из тех переменных, которые мы укажем при вызове.
Замечание2. Описание основных переменных программы можно теперь перенести непосредственно к началу главной части программы.
В результате внесённых нами изменений программа примет следующий вид:
//-------------------------------------------------------
// Процедура приветствия.
Procedure Hello;
Begin
Writeln('Данная программа предназначена');
Writeln('для вычисления суммы квадратов всех чисел,');
Writeln('расположенных в указанном Вами диапазоне.');
Writeln;
End;
//-------------------------------------------------------
// Процедура ввода исходных данных, использующая параметры.
Procedure Vvod(var p1, p2 : integer);
Begin Write('Введите начало диапазона. a='); Readln(p1);
Write('Введите окончание диапазона. b='); Readln(p2);
End;
//--------------------------------------------------------
// Процедура вычисления суммы квадратов, использующая параметры.
Procedure Calc(x,y : integer; var z : integer);
var i : integer; // Счётчик цикла
Begin z:=0;
for i:=x to y do z:=z + i*i;
End;
//---------------------------------------------------------
// Процедура вывода результатов вычисления.
Procedure Print(R : integer);
Begin
Writeln('Сумма квадратов всех целых чисел в указанном диапазоне равна - ', R);
End;
//----------------------------------------------------------
// Главная часть программы
Var a,b : integer; // Числа, вводимые пользователем
Rez : integer; // Результат вычислений
Begin Hello; // Вызов процедуры приветствия.
Vvod(a, b); // Вызов процедуры ввода исходных данных.
Calc(a,b,Rez); // Вызов процедуры вычисления.
Print(Rez); // Вызов процедуры печати результата.
End.
Этап пятый. Тестирование и доработка программы, если это необходимо.
Посмотрим, при всех ли значениях исходных данных будет работать наша программа.
Простой тест показывает, что НЕТ!
Например, при вводе диапазона [1, 1860] программа работает и выдаёт правильный результат: 2.146.682.110
Но при вводе диапазона [1, 1861]
результат становится отрицательным: -2.144.821.865
Почему это происходит?
Вспомним, в каком диапазоне могут находиться значения переменных типа «integer». Тип данных «integer» в языке Паскаль позволяет использовать данные, находящиеся в диапазоне от -2.147.483.648 до 2.147.483.647.
В нашей же программе, при вычислении 12+22+32+…+18602 получается результат, равный 2.146.682.110
При добавлении к этому результату ещё одного квадрата, квадрата числа 1861 мы получим 2.146.682.110 + 3.463.321, что равно 2.150.145.431, что превышает предельно допустимое значение 2.147.483.647.
В таких случаях происходит переполнение отведённой под хранение числа области памяти. Некоторые версии языка Паскаль выдадут при этом сообщение об ошибке. Но в большинстве версий осуществляется циклический перенос значений в отрицательную область, который происходит за счёт потери высшего двоичного разряда числа, выходящего за границу допустимых значений.
Как нам исправить программу так, что бы она выдавала корректное сообщение о невозможности вычислений в критических ситуациях?
Простейшее решение, предупреждающее пользователя о недопустимости ввода значений выше 1861, будет неверным.
Действительно, программа вполне способна вычислять значения сумм квадратов и больших, чем 1861 чисел, если нижняя граница вычисления будет много больше 1.
Например 18602+18612+18622 = 3.459.600 + 3.463.321 + 3.467.044 = 10.389.965. Что значительно меньше предельно допустимого значения и может быть вычислено программой.
Следовательно, необходимо продумать некоторый механизм, который будет контролировать превышение диапазона допустимых значений непосредственно в процессе вычислений.
Простая проверка текущей суммы на превышение допустимых значений:
if Z > 2.147.483.647
не сработает, так как сама сумма «Z» уже вычислена и уже имеет неверное значение.
Запись сложения непосредственно в условии проверки:
if ((Z + i*i) > 2.147.483.647 )
так же не даст нужного эффекта, так как при проверке условия предварительно будет вычисляться сумма, которая также будет вычислена неверно.
Но если перенести i*i в правую часть условия, то условие сработает верно. Превышения не будет ни в левой части условия, ни в правой:
if (Z > (2.147.483.647 – i*i))
Но как нам выдать соответствующее сообщение при печати результата в другой процедуре?
Воспользуемся тем, что сумма квадратов всегда является числом неотрицательным. Следовательно, если мы в качестве результата вернём любое отрицательное число, то это будет признаком невозможности вычисления результата.
В итоге, наша процедура вычисления примет следующий вид:
Procedure Calc(x,y : integer; var z : integer);
var i : integer; // Счётчик цикла
Begin
z:=0;
for i:=x to y do
if (z > (2147483647 – i*i))
then begin z:=-1;
break; // Оператор досрочной остановки цикла.
End
else z:=z + i*i;
End;
А в процедуре печати результата добавится проверка неотрицательности вычисленного значения:
Procedure Print(R : integer);
Begin
if R<0
then Writeln('При вычислении произошло переполнение')
else Writeln('Сумма квадратов всех целых чисел в указанном
диапазоне равна - ', R);
End;
Данная модификация программы уже корректно обработает введённые значения как для случая [1, 1861], так и для случая [1860, 1862].
Все ли критические ситуации мы предусмотрели?
Не совсем. Мы не учли возможность ошибки пользователя нашей программы при вводе им исходных данных. Пользователь может ошибочно ввести значение нижней границы диапазона вычисления, большую, чем верхняя граница. В этом случае наша программа не сможет вычислить никакого значения и выдаст результат, равный нулю, так как основной цикл в процедуре вычисления не сделает ни одного шага.
Но такой ответ не совсем корректен. В данном случае пользователю необходимо сообщить о том, что программа не может производить вычисления если границы диапазона вычисления перепутаны, либо автоматически поменять границы местами и вычислить результат.
Воспользуемся вторым вариантом.
Для этого нам придётся соответствующим образом доработать нашу процедуру ввода данных.
Во-первых, нам потребуется проверка корректности ввода.
Во-вторых, в случае обнаружения некорректных данных – осуществить обмен значений границ и выдать соответствующее сообщение пользователю.
В-третьих, для осуществления такого обмена нам потребуется дополнительная переменная.
В итоге, после всех введённых изменений, наша программа примет следующий вид:
//-------------------------------------------------------
// Процедура приветствия.
Procedure Hello;
Begin
Writeln('Данная программа предназначена');
Writeln('для вычисления суммы квадратов всех чисел,');
Writeln('расположенных в указанном Вами диапазоне.');
Writeln;
End;
//-------------------------------------------------------
// Процедура ввода исходных данных, использующая параметры.
// С проверкой корректности границ.
Procedure Vvod(var p1, p2 : integer);