SHPORA.net :: PDA | |
Main FAQ гуманитарные науки естественные науки математические науки технические науки Процедуры и функции ПРОЦЕДУРЫ И ФУНКЦИИ БЕЗ ПАРАМЕТРОВ Процедуры и функции В языке Turbo Pascal, как и во всех других современных языках программирования, имеется возможность разбиения программы на относительно независимые части, называемые подпрограммами. Разбивают программы на подпрограммы для того, чтобы: 1. Облегчить создание и отладку программы (маленькие независимые куски программы легче создавать и отлаживать, чем одну большую программу). 2. Сократить размер исходного текста и кода программы (в программе часто имеются повторяющиеся части, и если их вынести в подпрограммы, то размер исходного текста программы уменьшится). В разных языках программирования существуют разные правила работы с подпрограммами. Нас с вами интересует только Turbo Pascal версии 7.0. В этом языке существует два вида подпрограмм. Первый - это процедуры, второй - функции. Процедура – это подпрограмма, в которой выполняются определенные действия. Функция – это подпрограмма, в которой также выполняются определенные действия, но, в отличие от процедуры, в функции вычисляется и возвращается значение функции. Определения процедур и функций в Turbo Pascal очень похожи. Определение функций немного сложнее, чем определение процедур, поэтому мы начнем изучать работу с подпрограммами в Turbo Pascal именно с процедур. Простейшие процедуры Возьмем такую задачу. Необходимо ввести массив целых чисел, удалить из него минимальный элемент, вывести получившийся массив. Данная задача имеет следующий общий алгоритм решения: 1. Ввести массив. 2. Найти индекс минимального элемента. 3. Удалить элемент с найденным индексом. 4. Вывести массив. Таким образом, наша программа должна будет состоять из четырех относительно независимых кусков: 1-й кусок – ввод массива, 2-й кусок – поиск минимального элемента, 3-й кусок – удаление элемента, 4-й кусок – вывод массива. Сначала реализуем эту программу без использования подпрограмм: { Задание: ввести массив целых чисел, удалить из него минимальный элемент, вывести получившийся массив. Реализация программы без подпрограмм. } Program WithoutProcedureExample; Const {Определение констант} maxN = 20; {Максимально возможное количество элементов в массиве} Type {Определение типов} IndexEl = 1 .. maxN; {Индексы массива лежат в интервале от 1 до maxN} arrInt = array[IndexEl] of integer; {Массив целых чисел, содержащий до maxN элементов} Var {Объявление переменных} A : arrInt; {Массив} N : integer; {Количество элементов в массиве} I : IndexEl; {Переменная для сканирования массива} IndMin : IndexEl; {Номер минимального элемента массива} begin {1 – ввод массива} {1.1 - ввод количества элементов} repeat write('Введите n:'); readln(n); until (n >= 1) and (n <= maxN); {Из цикла выйти возможно только тогда, когда 1<=N<=maxN} {1.2 - ввод элементов массива поодиночке} for i := 1 to n do begin write('a[', i, ']='); readln(a[i]); end; {2 – поиск индекса минимального элемента} indMin := 1; for i := 2 to n do if A[i] < A[indMin] then IndMin := i; {3 - удаление элемента массива с индексом indMin} for i := indMin to n-1 do A[i] := A[i+1]; dec(n); {4 – вывод массива} writeln; for i := 1 to n do write(A[i]:3); writeln; end. {Конец программы WithoutProcedureExample} Теперь продемонстрируем на этой же задаче решение с использованием подпрограмм. Напрашивается мысль, а не разбить ли нашу программу на подпрограммы таким образом: 1-я подпрограмма – ввод массива, 2-я подпрограмма – поиск минимального элемента, 3-я подпрограмма – удаление элемента, 4-я подпрограмма – вывод массива. Сделаем это, причем будем использовать в программе простейшие подпрограммы-процедуры – так называемые процедуры без параметров. { Задание: ввести массив целых чисел, удалить из него минимальный элемент, вывести получившийся массив. Реализация с использованием процедур без параметров. } Program SimpleProcedureExample; Const {Определение констант} maxN = 20; {Максимально возможное количество элементов в массиве} Type {Определение типов} IndexEl = 1 .. maxN; {Индексы массива лежат в интервале от 1 до maxN} arrInt = array[IndexEl] of integer; {Массив целых чисел, содержащий до maxN элементов} Var {Объявление переменных} A : arrInt; {Массив} N : integer; {Количество элементов в массиве} I : IndexEl; {Переменная для сканирования массива} IndMin : IndexEl; {Номер минимального элемента массива} {Определение процедур} {==========================================} {ReadArray - процедура ввода массива с клавиатуры} procedure ReadArray; begin {1 - ввод количества элементов} repeat write('Введите n:'); readln(n); until (n >= 1) and (n <= maxN); {2 - ввод элементов массива поодиночке} for i := 1 to n do begin write('a[', i, ']='); readln(a[i]); end; end; {Конец процедуры ReadArray} {==========================================} {FindIndMin – процедура поиска индекса минимального элемента} procedure FindIndMin; begin {Ищем индекс min элемента} indMin := 1; for i := 2 to n do if A[i] < A[indMin] then IndMin := i; end; {Конец процедуры FindIndMin} {==========================================} {DeleteMin – процедура удаления минимального элемента} procedure DeleteMin; begin { Удаляем элемент массива с индексом indMin} for i := indMin to n-1 do A[i] := A[i+1]; dec(n); end; {Конец процедуры DeleteMin} {==========================================} {PrintArray – процедура вывода массива на экран} procedure PrintArray; begin { Выводим массив} writeln; for i := 1 to n do write(A[i]:3); writeln; end; {Конец процедуры PrintArray} {Основная часть программы} begin {1 – ввод массива} ReadArray; {2 – поиск индекса минимального элемента} FindIndMin; {3 – удаление элемента} DeleteMin; {4 – вывод массива} PrintArray; end. {Конец программы SimpleProcedureExample} Этот пример показывает, как сделать программу с использованием процедур без параметров. Для этого необходимо задачу разбить на относительно независимые куски. Затем каждый из этих кусков оформить в виде отдельной процедуры, а из основной части программы эти процедуры необходимо вызвать. Заметим, что все используемые в процедурах переменные и типы должны быть определены и объявлены выше определения процедур. Правила определения процедуры без параметров: 1. Из текста программы вырезается нужный кусок и переносится выше основной части программы (выше первого Begin). 2. Этот кусок текста программы заключается в операторные скобки Begin … End;. 3. Перед этим куском вставляется строка: procedure имя_процедуры; , где имя_процедуры – это идентификатор. Замечания: 1. Правила структурного программирования требуют, чтобы каждая процедура (и функция) была пояснена достаточным (ясным и полным) комментарием, который ставится перед самой процедурой. 2. Имя процедуры (и функции) должно быть смысловым. Заметим, что по отношению к процедурам (и функциям) это требование необходимо выполнять всегда (имеется в виду, что по отношению к именам переменных это требование иногда можно игнорировать, а вот по отношению к именам процедур и функций это требование игнорировать нельзя никогда). 3. Программу разбивают на подпрограммы в первую очередь для того, чтобы облегчить ее разработку и отладку. Облегчение основывается на том, что во время разработки и отладки приходится иметь дело не с “монстром” длиной в сотни и тысячи строк, а с набором независимых подпрограмм небольшого объема. Подпрограмму можно считать небольшой, если ее исходный текст не превышает 40-60 строк. В этом случае подпрограмма целиком помещается на одной странице распечатки. Еще лучше, если подпрограмма целиком помещается на экране компьютера - в этом случае ее текст не должен превышать 20 строк. 4. В тексте программы подпрограммы должны быть отделены друг от друга визуально. Обычно для этой цели между двумя подпрограммами вставляют не меньше двух пустых строк. Иногда к пустым строкам добавляют строки комментариев, подобных таким {=========================================}. Вызов процедур Для того чтобы процедура выполнила определенные в ней действия, ее нужно вызвать. Для вызова процедуры используется оператор процедуры. Оператор процедуры представляет собой имя вызываемой процедуры или функции. Выполнение оператора процедуры приводит к передаче управления на первую строку тела процедуры. После этого выполняются операторы, составляющие тело процедуры. Когда будет выполнен последний оператор тела подпрограммы, управление возвращается в точку вызова подпрограммы - на оператор, следующий за оператором процедуры. Пример: {1 } program CallProcedure; {2 } procedure HiWorld; {3 } begin {4 } Writeln('Привет,мир!'); {5 } Writeln('Hi World!'); {6 } end; {7 } {8 } begin {9 } Writeln('Это начало'); {10 } HiWorld; {11 } Writeln('Это конец'); {12 } end. После запуска этой программы управление передается на строку {8 } begin – начало программы. Затем выполняется оператор процедуры {9 } Writeln('Это начало'); - выводится строка ‘Это начало!’ Затем выполняется оператор процедуры {10 } HiWorld; - управление передается в процедуру HiWorld на строку {3 } begin – начало процедуры HiWorld. Затем выполняется оператор процедуры {4 } Writeln('Привет,мир!'); - выводится строка ‘Привет, мир!’ Затем выполняется оператор процедуры {5 } Writeln('Hi World!'); - выводится строка ‘Hi, World!’ Следующая строка {6 } end; - возвращает управление из процедуры HiWorld. Управление передается в точку вызова процедуры, на следующую за оператором процедуры строку {11 } Writeln('Это конец'); - выводится строка ‘Это конец!’ Следующая строка {12 } end. – возвращает управление из программы – программа завершает свою работу. Глобальные и локальные переменные Процедуры используются для борьбы со сложностью программ. Разбиение программы на процедуры позволяет разрабатывать (и отлаживать) программу по частям. В единое целое программа связывается двумя механизмами: вызовом процедур и общими переменными. Через вызов процедур производится передача управления в процедуры, а через общие переменные производится передача данных. Общие переменные называют глобальными переменными. Полувековая практика программирования показала, что сложность программы в целом зависит не только от сложности используемых алгоритмов и количества строк в тексте программы, от количества процедур и функций, но и от количества глобальных переменных. Чем больше глобальных переменных, тем более связанными являются процедуры: в одних процедурах значения инициализируются, в других изменяются, в третьих - используются. Причем любая глобальная переменная может быть модифицирована или использована в любой процедуре. Поэтому со временем (в 60-70 гг.) пришли к идее сократить до минимума, в идеале до 0, количество глобальных переменных. Первой альтернативой глобальным переменным являются локальные переменные. Локальные переменные объявляются, инициализируются и используются только в пределах одной процедуры или функции. Рассмотрим ранее приведенный пример. В нем используется 4 глобальные переменные: Var {Объявление глобальных переменных} A : arrInt; {Массив} N : integer; {Количество элементов в массиве} I : IndexEl; {Переменная для сканирования массива} IndMin : IndexEl; {Номер минимального элемента массива} В программе определено 4 процедуры: {1 – ввод массива} ReadArray; {2 – поиск индекса минимального элемента} FindIndMin; {3 – удаление элемента} DeleteMin; {4 – вывод массива} PrintArray; Рассмотрим использование всех глобальных переменных. Переменная A – массив. Элементы массива вводятся в процедуре ReadArray, массив используется в процедурах FindIndMin и PrintArray и изменяется в процедуре DeleteMin. То есть можно сказать, что значение массива A передается из процедуры ReadArray в процедуру FindIndMin, затем в DeleteMin, где он изменяется, и измененное значение передается в PrintArray. Следовательно, переменную A сделать локальной нельзя – она должна быть глобальной. Переменная N – количество элементов в массиве. Значение переменной вводится в процедуре ReadArray, используется в процедурах FindIndMin и PrintArray и изменяется в процедуре DeleteMin. Следовательно, переменную N, так же, как и массив A, сделать локальной нельзя – она должна быть глобальной. Переменная I – переменная для сканирования массива. Эта переменная инициализируется, изменяется и используется в каждой из четырех процедур. Между процедурами значение этой переменной не передается. Следовательно, переменную N можно сделать локальной. Переменная IndMin – индекс минимального элемента массива. Эта переменная получает значение в процедуре FindIndMin и используется в процедуре DeleteMin. Следовательно, ее нельзя сделать локальной – она должна быть глобальной. Таким образом, из четырех переменных одну можно (и нужно) сделать локальной. Объявление глобальных и локальных переменных Глобальные переменные объявляются, вне какой либо процедуры, в разделе VAR. После объявления глобальная переменная доступна (то есть может быть использована) во всех процедурах, описанных ниже. В нашем примере все четыре переменные объявлены как глобальные, они доступны во всех четырех процедурах. Локальные переменные объявляются внутри процедуры (или функции), в разделе VAR. Замечание: если в подпрограмме объявлена локальная переменная, имя которой совпадает с именем глобальной переменной, то внутри этой подпрограммы глобальная переменная будет недоступна. Говорят, что локальная переменная перекрывает одноименную глобальную переменную. Для примера объявим переменную I как локальную переменную внутри процедуры PrintArray: {PrintArray – процедура вывода массива на экран} procedure PrintArray; Var {Объявление локальных переменных} I : IndexEl; {Счетчик цикла – локальная переменная} begin { Выводим массив} writeln; for i := 1 to n do write(A[i]:3); writeln; end; {Конец процедуры PrintArray} Функции без параметров Как говорилось ранее, в Turbo Pascal существует два вида подпрограмм – процедуры и функции. Функции отличаются от процедур тем, что в них вычисляется и возвращается значение. Процедуру можно превратить в функцию, если в ней вычисляется какое-то (желательно одно единственное) значение. При этом превращении сокращается количество глобальных переменных, через которые передаются данные из процедуры (и это очень хорошо). Проанализируем ранее рассмотренную задачу. В ней имеется 4 процедуры: {1 – ввод массива} ReadArray; {2 – поиск индекса минимального элемента} FindIndMin; {3 – удаление элемента} DeleteMin; {4 – вывод массива} PrintArray; В функцию можно (и нужно) переделать процедуру FindIndMin – единственным действием в этой процедуре является вычисление индекса минимального элемента массива. После превращения процедуры в функцию эта подпрограмма примет такой вид: {FindIndMin –функция поиска индекса минимального элемента} {Возвращаемое значение – индекс минимального элемента} Function FindIndMin:IndexEl; Var Ind : IndexEl; {Локальная переменная, в которой хранится индекс минимального элемента массива до возвращения из функции} I : IndexEl; {Локальная переменная, счетчик цикла} begin {Ищем индекс минимального элемента} Ind := 1; for i := 2 to n do if A[i] < A[ind] then Ind := i; FindIndMin := Ind; {Функция возвращает найденный индекс} end; {Конец функции FindIndMin} Правила определения функций без параметров (из процедуры без параметров): 1. В процедуре определяется локальная переменная, в которой хранится вычисляемый в этой процедуре параметр. Эта переменная в процедуре заменяет соответствующую ей глобальную переменную. 2. Слово Procedure заменяется словом Function. 3. После имени функции вставляется двоеточие и тип возвращаемого значения. 4. В конце тела функции необходимо присвоить имени функции значение локальной переменной, хранящей вычисленный параметр (смотри пункт 1). Вызов функции Для того чтобы функция выполнила определенные в ней действия, ее нужно вызвать. Для вызова функции используется оператор процедуры, в тексте программы пишется имя этой функции. Если мы хотим сохранить значение, возвращаемое функцией, то при ее вызове мы должны присвоить возвращенное из функции значение какой-нибудь переменной. Пример: Выведем индекс минимального элемента массива. Для этого воспользуемся определенной выше функцией FindIndMin. … Var IndexMinEl : IndexEl; {Локальная переменная, в которую занесем возвращенное из функции FindIndMin значение} … begin … {Ищем индекс минимального элемента} IndexMinEl := FindIndMin; {Выводим на экран индекс минимального элемента массива} Writeln(‘Минимальный элемент массива имеет индекс = ’, IndexMinEl); … Предположим, что значение, возвращенное функцией, нужно использовать только один раз, для вывода его на экран. Тогда можно обойтись и без переменной IndexMinEl. Тогда текст примера сократится до такого куска: … begin … {Ищем индекс минимального элемента и выводим его на экран} Writeln(‘Минимальный элемент массива имеет индекс = ’, FindMinEl); … Выполнение функций от выполнения процедур отличается только тем, что после окончания работы функции ее результат заносится в соответствующую переменную. Пример: Вычислим значение тангенса по введенному значению угла в градусах. {1 } program CallFunction; {2 } var {3 } x : real; {Значение введенного угла в градусах} {4 } {5 } function tg:real; {6 } var {7 } xRad : Real; {Значение угла в радианах} {8 } begin {9 } xRad := x * Pi / 180.0; {Перевод градусов в радианы} {10 } tg := sin(xRad) / cos(xRad); {11 } end; {12 } {13 } var {14 } y : real; {15 } begin {16 } Write('Введите x = '); {17 } Read(x); {18 } y := tg; {19 } Writeln( 'tg(', x:0:2, ')=', y:0:4); {20 } end. Эта программа начинает свою работу со строки {15 } begin Затем выполняется {16 } Write('Введите x = '); и {17 } Read(x); Затем в строке {18 } y := tg; - вызывается функция tg, в результате чего управление передается на строку {8 } begin Затем выполняется {9 } xRad := x * Pi / 180.0; {Перевод градусов в радианы} Затем в строке {10 } tg := sin(xRad) / cos(xRad); - вычисляется значение тангенса, которое в строке {11 } end; - возвращается в точку вызова функции tg – в строку {18 } y:=tg; -где переменной Y присваивается возвращаемое из функции значение тангенса. Следующей строкой выполняется {19 } Writeln( 'tg(', x:0:2, ')=', y:0:4); И, наконец, в строке {20 } end. - управление из программы возвращается операционной системе – программа завершает свою работу. Пример использования процедур и функций без параметров Мы рассмотрели правила оформления и использования подпрограмм (т.е. процедур и функций) без параметров. Теперь используем выше полученные знания для решения следующей задачи. Необходимо посчитать, сколько разных элементов хранится в массиве. Пример: массив 1 4 3 2 4 3 1 3 5 разные элементы 1 4 3 2 5 всего их 5 Вот текст программы: {Подсчитать, сколько разных элементов хранится в массиве. Одномерный массив целых чисел. Количество элементов от 1 до 20. В программе использовать процедуры и функции без параметров. До минимума постараться сократить использование глобальных переменных. } Program SimpleFunctionExample; Const maxNumEl = 20; {Максимально возможное количество элементов в массиве} Type IndexElement = 1 .. maxNumEl; {Индексы массива} ArrayInteger = array[IndexElement] of integer; {Массив целых чисел} Var {*** Объявление глобальных переменных ***} Arr : ArrayInteger; {Обрабатываемый массив} NumEl : integer; {Количество элементов в обрабатываемом массиве} {*** Определение процедур и функций: ***} {==========================================} {ReadArray - процедура ввода массива с клавиатуры} {Данные вводятся в массив Arr. В переменную NumEl заносится количество элементов в массиве Arr} procedure ReadArray; var i : IndexElement; {*Счетчик цикла*} begin writeln; {*Ввод количества элементов*} repeat write('Введите количество элементов в массиве:'); readln(NumEl); until (NumEl >= 1) and (NumEl <= maxNumEl); {*Ввод элементов массива*} write('Введите элементы массива : '); for i := 1 to NumEl do read(Arr[i]); end; {*Конец процедуры ReadArray*} {==========================================} {PrintArray - процедура вывода массива на экран} {Данные берутся из массива Arr. В переменной NumEl хранится количество элементов в массиве Arr} procedure PrintArray; var i : IndexElement; {*Счетчик цикла*} begin writeln; write('Массив : '); for i := 1 to NumEl do write(Arr[i]:3); writeln; end; {*Конец процедур PrintArray*} {==========================================} {NumberOfDifferentElements - функция, вычисляющая количество различных элементов в массиве.} {Данные берутся из массива Arr. В переменной NumEl хранится количество элементов в массиве Arr} Function NumberOfDifferentElements:integer; var i, j : IndexElement; {*Счетчики циклов*} num : integer; {Количество разных элементов} isDifferent : boolean; {Элемент является отличным от все предыдущих} begin {Для подсчета количества разных элементов массива используется следующий алгоритм: - количество различных элементов обнуляем; - начиная с первого элемента, по очереди берем все элементы и сравниваем их со всеми предшествующими. Если взятый элемент равен хотя бы одному из предшествующих, то этот элемент не является новым - мы такой уже учли; - если же он ни одному из предшествующих не равен, то увеличиваем количество РАЗЛИЧНЫХ элементов на 1.} num := 0; for i := 1 to NumEl do begin isDifferent := true; {i-ый элемент является отличным от предыдущих} for j := i - 1 downto 1 do if Arr[i] = Arr[j] then begin isDifferent := false; {i-ый элемент совпадает} break; {с одним из предыдущих} end; if isDifferent then num := num + 1; end; NumberOfDifferentElements := num; {*Возвращаем вычисленное количество разных элементов*} end; {*Конец функции NumberOfDifferentElements*} {*Тело программы*} begin {*Вводим массив Arr*} ReadArray; {*Выводим массив Arr*} PrintArray; {*Вычисление и вывод количества различных элементов массива*} Write('В введенном массиве различных элементов : '); writeln(NumberOfDifferentElements); end. {*Конец программы *} В данном примере показан более-менее “правильный” стиль оформления исходного текста программы и его комментирования. Комментарии, помеченные символами *** ***, в реальной программе не пишутся. Здесь они вставлены по причине того, что этот текст - учебный пример. Комментарии, помеченные символами * * , в реальной программе могут писаться или не писаться, в зависимости от требований к качеству и количеству комментариев. Комментарии, не помеченные никакими значками, писать нужно ОБЯЗАТЕЛЬНО, если этот исходный текст программы в дальнейшем (спустя какое-то время) кто-то будет читать. |