Персональная страничка
| ||
Предыдущий раздел:
Следующий раздел:
Хорошо написанная подпрограмма должна быть максимально независима от остальной программы. Для этого в ней не должно быть обращений к глобальным переменным, а всю необходимую информацию она должна получать и отдавать через параметры. Составление заголовков (то есть, по сути, выбор того, что сделать параметрами), таким образом, требует понимания, какие данные необходимы для решения оформляемой в виде подпрограммы подзадачи (входные данные), и в каком виде лучше всего представить результат (выходные данные).
Простейший пример: требуется возвести число в квадрат. Какие данные для этого необходимы? Необходимо само число, которое будем возводить, и все. Что будет на выходе? Опять же одно число – квадрат того, что на входе. Данные на входе должны быть параметрами процедуры или функции (как правило, параметрами-значениями). Выходные данные должны записываться в параметры-переменные или в значение функции. Соответственно есть следующие варианты написания подпрограмм для решения этой задачи:
procedure NewSqr1(x: real; var x2: real); {Входные данные передаются через параметр-значение x, результат записывается в параметр-переменную x2} begin x2:=x*x; end; function NewSqr2(x: real): real; {Входные данные передаются через параметр-значение x, результат записывается в значение функции} begin NewSqr2:=x*x; end; procedure NewSqr3(var x: real); {Одна и та же переменная служит для хранения входных и выходных данных} begin x:=x*x; end;
Пусть есть две переменные:
a, b: real;
Записать в переменную b квадрат переменной a теперь можно следующими способами:
1. NewSqr1(a, b);
2. b:=NewSqr2(a);
3. b:=a; NewSqr3(b);
Некоторые студенты засовывают в заголовок описания всех переменных, которые им требуются внутри процедуры, явно путая назначение параметров и локальных переменных. Так делать нельзя!
Итак:
1) Параметрами должны быть только входные или выходные данные.
2) Все переменные, значение которых неизвестно или несущественно в момент начала работы процедуры и значения которых не важны после окончания работы, должны быть локальными переменными.
Пример: функция для расчета факториала. Что должно быть входе? Целое число, факториал которого хотим подсчитать. Что на выходе? Значение факториала – тоже целое число. Таким образом, заголовок будет выглядеть так:
function Factorial(n: integer): integer;
При расчете потребуется переменная счетчик цикла, а также переменная, в которой будет накапливаться произведение, но все они должны быть локальными переменными.
Теперь пара слов, почему так важно правильно составлять заголовки, определяя формат данных на входе и выходе. Как уже говорилось, разработка любой сложной программы требует разбиения задачи на подзадачи, решения которых оформляются в виде процедур или функций. Далее программист сосредотачивается на решении отдельной подзадачи. Новичкам бывает трудно это сделать – начинаешь решать одну подзадачу, оказывается, для решения требуется решить другую, потом третью и т.д. Так и пытаются решить задачу целиком, что, начиная с определенного уровня сложности, просто невозможно.
Выход из этой ситуации следующий. Если, решая одну задачу, вы видите, что требуется решить подзадачу, необходимо определить, какие данные необходимы для ее решения и какие данные будут являться результатом расчетов. После этого вы можете составить заголовок процедуры или функции, решающей подзадачу. Имея заголовок, можно отложить написание тела подпрограммы на потом, а в программу вставить вызов процедуры или функции так, будто она уже написана. В результате вы решаете основную задачу, не отвлекаясь на возникающие по ходу подзадачи.
Последний вопрос. Имеется подзадача. Что писать процедуру или функцию? Функция пишется тогда, когда результат имеет простой (не структурный) тип, то есть не является массивом (а также файлом, записью, классом или множеством). Кроме того, возможен вариант, когда часть выходных данных передается через параметры, а часть записывается в значение функции.
Пример 1: Опишите вещественнозначный массив и вещественную переменную, напишите заголовок подпрограммы, подсчитывающий сумму элементов массива и ее вызов с использованием описанных вами переменных.
Решение:
Задаем обязательные вопросы. Что на входе? Массив. Что на выходе? Сумма элементов – одно число. Входные данные передадим через параметр, выходные запишем в значение функции. Правильный ответ к задаче выглядит так:
const n = 10; var a: array [1..n] of real; s: real; … function Sum(var x: array of real): real; … s:=Sum(a);
Не будет ошибкой и оформление подпрограммы в виде процедуры c записью результата в параметр переменную:
procedure Sum(var x: array of real; var s: real); … Sum(a, s);
Пример 2: Напишите заголовок подпрограммы для нахождения суммы элементов массива, индексы которых лежат в заданном диапазоне. Напишите также вызов этой подпрограммы, описав необходимые для этого переменные.
Решение:
Задаем обязательные вопросы. Что на входе? На входе обязательно массив и требуется также задать диапазон индексов. Для этого можно сказать, начиная с какого индекса, будем суммировать и по какой (то есть задать два целых числа). Что на выходе? Если удастся подсчитать сумму, то на выходе сумма элементов (то есть число). Однако не лишним будет сделать «защиту от дурака». Что если в массиве 10 элементов, а при вызове требуется подсчитать сумму с 1-го по 20-й. Предупредить, что такое невозможно и элементов с такими индексами нет можно с помощью переменной-флага – логической переменной, которая равна true, если все в порядке или false, если расчет невозможен. Таким образом, на выходе вещественное число и логическое значение.
Ответ: Правильных вариантов масса. Например, оформляем в виде функции:
const n = 10; var a: array [1..n] of real; s: real; n1, n2: integer; f: boolean; … function Sum(var x: array of real; n1, n2: integer; var flag: boolean): real; … s:=Sum(a, n1, n2, f);
Другой правильный вариант:
function Sum(var x: array of real; n1, n2: integer; var s: real): boolean; … f:=Sum(a, n1, n2, s);
Еще правильный вариант:
procedure Sum(var x: array of real; n1, n2: integer; var s: real; var f: boolean); … Sum(a, n1, n2, s, f);
Пример 3: Создать подпрограмму, увеличивающую все элементы массива в два раза.
Обязательные вопросы. Что на входе? Массив. Что на выходе? Такой же массив, увеличенный в 2 раза.
const n = 10; var a: array [1..n] of real; … procedure Double(var x: array of real); … Double(a);
Здесь один и тот же массив x используется как входная и выходная переменная.
Еще правильный вариант:
const n = 10; var a, b: array [1..n] of real; … procedure Double(x: array of real; var y: array of real); … Double(a, a);
Следующий вариант вызова не «портит» исходный массив:
Double(a, b);
Задания на составление заголовков
1. Напишите заголовки подпрограмм для решения идущих ниже задач. Запишите также вызов этих подпрограмм в основной программе, описав необходимые для этого переменные (все по аналогии с приведенными выше примерами). Если есть возможность оформить подпрограмму и как процедуру и как функцию, запишите ответ в обоих вариантах.
1) Нахождение факториала.
2) Возведение вещественного числа в целую степень.
3) Расчет среднего арифметического элементов массива.
4) Перестановка элементов массива в обратном порядке.
5) Нахождение максимального элемента числового массива.
6) Нахождение индекса максимального элемента массива.
7) Нахождение индекса максимального элемента среди последних n элементов массива.
8) Нахождение индекса максимального элемента среди первых n элементов массива.
9) Нахождение расстояния между точками в многомерном пространстве.
10) Нахождение модуля вектора.
11) Нахождение скалярного произведения.
12) Нахождение суммы двух векторов.
13) Нахождение векторного произведения двух векторов. Векторное произведение может быть рассчитано только для 3-мерных векторов. Если вектор не 3-мерный передавайте информацию о невозможности расчета через флаговую переменную.
14) Нахождение смешанного произведения.
15) Нахождение суммы двух комплексных чисел.
16) Нахождение угла между двумя векторами.
17) Вычисление гипотенузы по известным катетам.
18) В одном массиве содержатся фамилии, в другом год рождения соответствующих людей. Найти по
фамилии год рождения.
19) При наличии тех же массивов, что и в предыдущей задаче узнать, сколько человек родились в заданном году.
20) При наличии тех же массивов распечатать фамилии всех людей, возраст которых лежит в заданном диапазоне.
2. Придумайте задачу, для решения которой потребуются подпрограммы со следующими заголовками:
procedure P1(a, b: integer); procedure P2(var a, b: real); procedure P3(a, b: real; var c: real); function F4(x: real): real; function F5(x: real): integer; function F6(x, y: real): boolean; function F7(x: array of real): real; procedure P8(x, y: array of real; var z: array of real); function F9(var s: array of string; var x: array of integer; z: integer; var flag: boolean): integer; function F10(var s1, s2: array of string; var n: integer): boolean;
Следующий раздел:
Предыдущий раздел:
Здравствуйте, Тарас Викторович, проверьте, пожалуйста, заголовки:
1)a) var
x,y: integer;
procedure P1 (a,b: integer);
…
P1(x,y);
б) function F1(a: integer): integer;
…
y:=F1(x);
2)a) var
x: real;
y: integer;
procedure P2 (var a,b: real);
var
i: integer;
…
P2(x,y);
б)function F2 (var a: real): real;
…
y:=F2(x);
3)a) var
x: array[1..n] of integer;
sr: integer;
procedure P3 (a: array of integer; s: integer);
var
i,d: integer;
…
P3(x,sr);
б) function F3 (a: array of integer): integer;
…
sr:=F3(a);
Это что-то не то.
1) Для нахождения факториала
не очень удачно. Потому что процедура никуда не сможет записать результат. Надо, например,
Функция — нормальная.
2) Для возведения в целую степень, логично было бы иметь параметр целого типа. И опять же процедуре некуда записывать результат.
3) Среднее арифметическое даже целочисленного массива может запросто оказаться не целым.
Хороший материал. Между тем.
Если работаем с массивами, в среде Pascal ABC.NET параметры процедур и функций следует описывать с учетом
необходимости описания типа массива (см. пример 3).
Не работают описания типа:
Program CH_11_12_ex3;
const
N=10;
var
a: array [1..N] of real;
i: integer;
procedure Double(var x: array of real);
Следует описывать так:
Program CH_11_12_ex3;
const
N=10;
type
Tmass=array [1..N] of real;
var
a: Tmass;
i: integer;
procedure Double(var x: TMass);
var
i: integer;
begin
for i:=1 to N do
x[i]:=x[i]*x[i];
end; и т.д.
Тарас Викторович сам писал об этом ранее.
Такое чувство, что я один из первых читателей главы 11.12 (слишком мало коментов).
Не слишком ли корявый стиль?:
Пример 2 гл. 11.12:
Program CH_11_12_ex2;
const
n = 10;
n1= 2;
n2= 7;
type
TMass=array [1..N] of real;
var
a: TMass;
s: real;
i: integer;
f: boolean;
function Sum(var x: TMass; n1, n2, N: integer; var flag: boolean): real;
var
i: integer;
s: real;
begin
if (0<=n1) and (n1<=N) and (0<=n2) and (n2<=N) then
begin
s:=0;
for i:=n1 to n2 do
s:=s+x[i];
Sum:=s;
end else
flag:=false;
end;
begin
for i:=1 to N do
a[i]:=trunc(10*random);
for i:=1 to N do
write(a[i]:3);
writeln;
f:=true;
s:=Sum(a, n1, n2, N, f);
if f=true then
writeln(' s=', s)
else
writeln(' Ошибка индексации!: индексы массива:[1..N], 0<=n1<=n2<=N');
end.
Понятен принцип составления скелета программы с использованием заголовков подпрограмм. А вот для самоконтроля и лучшей усвояемости материала желательно затем составлять полные программы с проверкой их работоспособности.
Вариант решения примера 2:
Program CH_11_12_ex2;
const
n = 10;
n1= 2;
n2= 7;
type
TMass=array [1..N] of real;
var
a: TMass;
s: real;
i: integer;
f: boolean;
function Sum(var x: TMass; n1, n2, N: integer; var s: real): boolean;
var
i: integer;
begin
if (0<=n1) and (n1<=N) and (0<=n2) and (n2<=N) then
begin
Sum:=true;
s:=0;
for i:=n1 to n2 do
s:=s+x[i];
end else
Sum:=false;
end;
begin
for i:=1 to N do
a[i]:=trunc(10*random);
for i:=1 to N do
write(a[i]:3);
writeln;
f:=Sum(a, n1, n2, N, s);
if f=true then
writeln(' s=', s)
else
writeln(' Ошибка индексации!: индексы массива:[1..N], 0<=n1<=n2<=N');
end.
Угол между двумя векторами:
Program CH_11_12_z16;
const
N=3;
type
TMass1 = array[1..N] of real;
TMass2 = array[1..N] of real;
var
a: TMass1;
b: TMass2;
z, fi, M1, M2, Sp: real;
procedure ReadTMass(var x: TMass1; var y: TMass2; N: integer);
function Modul(var x: TMass1; var y: TMass2; N: integer; var s1: real; var s2: real);
function Scalar(x: TMass1; y: TMass2; N: integer): real;
function Cos_fi(sс, x, y: real): real;
function Arc(z1: real): real;
begin
ReadTMass(a, b, N);
Modul(a, b, N, M1, M2);
Sp:=Scalar(a, b, N);
z:=Cos_fi(Sp, M1, M2);
fi:=Arc(z);
writeln(‘ z=’, z:8:6, ‘ ‘, ‘fi=’, fi:7:5);
end.
Дополнение к Program CH_11_12_z16 (расшифровка):
function Arc(z1: real): real;
const
n=100000;
eps=1E-4;
var
i: integer;
x, z2, delta: real;
begin
x:=0;
i:=0;
repeat
x:=x+Pi/n;
z2:=cos(x);
i:=i+1;
delta:=abs(z1-z2);
until delta<eps;
Arc:=x;
end;
В подпрограмме function Arc переставить операторы:
repeat
z2:=cos(x);
x:=x+Pi/n;
i:=i+1;