yandex rtb 1
ГоловнаЗворотній зв'язок
yande share

Основы программирования

8. ПОДПРОГРАММЫ

1. Понятие о структурном программировании. Задачи и программы, которые мы рассматриваем в этом конспекте, имеют учебный характер и не являются сложными и большими. На практике задачи, соответствующие алгоритмы и программы обычно являются сложными или громоздкими. В таких случаях следует помнить об основных принципах структурного программирования.

Структурное программирование - это концепция программирования, которая предусматривает:

1. Предварительный анализ сложной задачи или громоздкого алгоритма с целью разбивки     ее (его) на отдельные простые части.

2. Последовательную детализацию всех частей и составления соответствующих подпрограмм.

3. Использование трех базовых конструкций языка (простой, ветвления, цикла) при составлении каждой подпрограммы.

4. Написание программ, понятных для людей, которые будут с ними работать.

5. Минимальное использование команд перехода.

6. Систему средств проверки правильности программы: логический анализ программы до ее выполнения, перекрестная проверка программ, коллективная работа над созданием сложных программ и т.п.

Объясним некоторые из этих принципов.

Детализация сверху донизу. При решении сложной задачи алгоритм разбивают на части, выделяют основные подзадачи, которые в свою очередь разбивают на части низшего уровня. Каждую часть разрабатывают отдельно: сначала уточняют (программируют) части верхнего уровня, а позже — низшего. Это называется анализом сверху донизу и пошаговой детализацией алгоритма. В конце отдельные части стыкуют между собой.

Метод пошаговой детализации имеет важное значение не только в информатике. Один человек может руководить разработкой большого алгоритма (созданием бюджета государства, строительством космической станции и т.п.), поручая многим исполнителям детализацию вспомогательных алгоритмов (выполнение отдельных работ).

Принципом пошаговой детализации следует руководствоваться во время решения задач по информатике, в курсе математики и физики, по другим предметам, а также в быту. Об этом принципе не следует забывать и в будущем.

Принцип пошаговой детализации, известный с давних пор, применяли с успехом в военном деле. Провозглашенный древними римлянинами лозунг «Divide and conquer» («Разделяй и властвуй») хорошо усвоили Александр Македонский и Наполеон Бонапарт. Последний сумел в 1805 г. под Аустерлицем разделить превосходящую австрийскую армию на отдельные подразделения и разгромить их поочередно. Наполеон был завоевателем и вошел в историю как гениальный полководец. Сегодня, чтобы подчинить мир, не надо воевать. Это с успехом доказали большие компьютерные фирмы, такие как Microsoft Corporation, IBM Corporation и другие, где над отдельными проектами работают сотни, а то и тысячи специалистов, слаженную деятельность которых обеспечивают руководители проектов (супервизоры), хорошо усвоившие этот принцип.

Базовые конструкции. Целесообразно выяснить, какие конструкции и элементы языка можно использовать во время программирования каждой части. Напомним, что в распоряжении пользователя есть три базовые (основные) алгоритмические конструкции: 1) простая; 2) ветвление; 3) цикл. В 1964 году была доказана теорема о том, что алгоритм произвольной сложности можно построить на базе этих трех конструкций.

Необходимо избегать команд перехода. Бессистемное использование команд перехода (в особенности к предыдущей части программы), которые усложняют чтение программы, нужно сводить к минимуму. Этого можно достичь с помощью таких конструкций языка, как условная команда if-then-else, команда многозначного выбора case, команды цикла while и подпрограмм с параметрами.

Есть возможность досрочно выйти с некоторой конструкции с помощью таких команд выхода: exit, break, continue и halt.

Необходимо проверять правильность программы до её выполнения на компьютере. Для этого используют, в частности, прием трассировки программы - построения таблицы значений всех переменных.

Подпрограммы предназначены для реализации алгоритмов решения отдельных подзадач. Они дают возможность реализовывать концепцию структурного программирования.

Различают два вида подпрограмм - подпрограммы-процедуры и подпрограммы-функции. Подпрограммы делятся на стандартные и подпрограммы пользователя. Стандартные подпрограммы пользователь не создает, они находятся в стандартных модулях System, Crt, Dos, Graph и т.д. Подпрограмма пользователя - это поименованная группа команд, которую создают и описывают в основной программе в разделах procedure или function. Подпрограмму можно вызвать из любого места программы необходимое количество раз.

2. Процедуры (procedure). Общий вид процедуры:

procedure <имя> (<список формальных параметров>);

               <разделы описаний и объявлений процедуры>;

                          begin

                                         <раздел команд процедуры>

                          end;

В списке формальных параметров перечисляют переменные и указывают их типы. Различают параметры—аргументы (другой термин: параметры-значения) — входные данные для процедуры, и параметры—результаты (другой термин: параметры-переменные), через которые можно возвращать результаты работы процедуры в основную программу. Перед списками параметров-результатов каждого типа записывают слово var. Заметим, что массивы фиксированных размеров в этих списках описывать при помощи слова array нельзя (см. ниже образцы программ).

Разделы описаний и объявлений в подпрограммах имеют такую же структуру, как и в основной программе.

Пример. Рассмотрим процедуру Сеnа, которая определяет стоимость телефонного разговора с поминутной оплатой 0.6 руб. + 20% НДС.

procedure Cena(k : integer; var с : real);

begin

с:= k * 0.6;

с:= с + 0.2 * с;

end;

В приведенном примере k - формальный параметр-аргумент (это количество минут разговора), с - формальный параметр-результат (сумма, которую необходимо уплатить за k минут разговора).

Процедуру можно вызвать из раздела команд основной программы или другой подпрограммы с помощью команды вызова:

<имя процедуры> (<список фактических параметров>);

Параметры, которые задают в команде вызова процедуры, называются фактическими. Фактическими параметрами-аргументами могут быть константы, переменные, выражения, а параметрами-результатами - только переменные. Типы данных в команде вызова не указывают.

Между фактическими и формальными параметрами должно быть соответствие по количеству и типам. Имена соответствующих пар фактических и формальных параметров могут не совпадать.

Команда вызова работает так: значения фактических параметров присваиваются соответствующим формальным параметрам процедуры, выполняется процедура, определяются параметры-результаты, значения которых присваиваются (возвращаются) соответствующим фактическим параметрам команды вызова.

Переменные, описанные в разделе var основной программы, называются глобальными. Они действуют во всех подпрограммах данной программы. Переменные, описанные в разделе описания конкретной процедуры, называются локальными. Они действуют только в данной процедуре.

Процедуры могут получать и возвращать значения не только с помощью параметров—результатов, но и через глобальные переменные. Поэтому списков параметров в процедуре может и не быть.

Задача 1. Решить задачу из предыдущего пункта о количестве вызовов на АТС, используя три процедуры: 1) для определения количества вызовов за каждую секунду (назовём процедуру Kolvyzov); 2) для вычисления количества вызовов за первые 10 секунд (Summavyzov); 3) для определения наибольшего количества вызовов за некоторую секунду (MaxKolvyzov). Использовать функцию random.

program ATS 1;

uses Crt;

type        vyzov = array [1..10] of integer;

var         у : vyzov;

max, s : integer;

procedure Kolvyzov(var у : vyzov); {Процедура Kolvyzov}

var і : integer;                        {определяет количество вызовов}

begin                                      {за каждую секунду}

for і := 1 to 10 do

begin

y[i] := random(i);

writeln('y(' ,i,') = ', y[i]:5);

end;

end;

procedure Summavyzov(y : vyzov; var s : integer);

{Процедура вычисляет количество вызовов}

var і : integer;                        {за первые 10 секунд}

begin

s:=0;

for і := 1 to 10 do s := s + y[i];

writeln('Сумма вызовов S = ', s:3);

end;

procedure MaxKolvyzov(y : vyzov; var max : integer);

var і : integer;                        {Процедура MaxKolvyzov определяет}

begin                                      {наибольшее количество вызовов}

max := y[l];                 {за некоторую секунду}

for і := 2 to 10 do

if max < y[i] then max := y[i];

write('Максимальное количество вызовов за одну ');

writeln('секунду равно ', mах:3)

end;

begin

clrscr;

randomize;

Kolvyzov(y);              {Вызываем процедуру Kolvyzov}

Summavyzov(y, s);     {Вызываем процедуру Summavyzov}

{Вызываем процедуру MaxKolvyzov}

MaxKolvyzov(y, max);

readln

end.

Задание 1. Решите задачу № 14 своего варианта.

3. Функции (function). В отличие от процедуры функция может возвращать в точку вызова только один результат простого стандартного типа.

Функцию описывают так:

function <имя>(<список формальных параметров>) :

                                                     <тип функции>;

             <разделы описаний и объявлений функции>;

   begin

            <раздел команд функции, где должна быть такая команда:

                                        имя:=выражение>

   end;

В разделе команд функции должна быть команда присваивания имени функции значения некоторого выражения. Результат функции возвращается в основную программу через её имя (как и в случае использования стандартных функций, таких как sin, cos). Вызов функции осуществляется только из выражений с помощью указателя функции:

<имя>(<список фактических параметров>).

Пример. Опишем функцию для вычисления tg(x) и вычислим значение выражения tg(x)+ctg(x)+tg2(x).

program Myfunc;

uses Crt;

var x, у : real;

function tg(x : real) : real;

begin

tg := sin(x) / cos(x)

end;

begin

clrscr;

writeln('Введите х');

readln(x);

у := tg(x) + 1 / tg(x) + sqr(tg(x));

writeln(‘y =’ y:5:2);

readln

end.

Задача 2. Решить задачу о производстве конфет на фабрике из предыдущего пункта, используя функции и процедуры пользователя.

program Fabrikal;

uses Crt;

const n = 5;

type     zatraty = array [l..n, l..n] of real;

var       imin : integer;

a : zatraty;

function func(i, j : integer) : real;

begin

func := 2 * abs(sin(i)) + j;

end;

procedure Table(var a : zatraty);

var i, j : integer;

begin

writelnC Вид сырья');

writeln('         1     2     3     4      5');

for. і := 1 to n do         {Создаём таблицу затрат]

begin

write(i, ' сорт');

for j := 1 to n do

begin

{Используем созданную функцию}

a[i, j] := func(i, j);

{Выводим элементы і-ой строки}

write(a[i,j]:7:2);

end;

writeln            {Переходим на новую строку}

end;

end;

procedure MinSyr(a : zatraty; var imin : integer);

var       i, j : integer;

min : real;

begin

imin := 1; .

min:=a[l,3];

for і := 2 to n do

if a[i, 3] < min then

begin

min := a[i, 3];

imin := і

end;

writeln('Меньше всего сырья третьего вида ');

write('необходимо для производства конфет ');

writeln( imin, ' сорта')

end;

begin

clrscr;

Table(a);                                 {Вызываем процедуру Table}

MinSyr(a, imin);                     {Вызываем процедуру MinSyr}

readln

end.

Задание 2. Решите задачу № 15 своего варианта с использованием процедур.

4. Рекурсивные функции. Рекурсией называется алгоритмическая конструкция, где подпрограмма вызывает сама себя. Рекурсия дает возможность записывать циклические алгоритмы, не используя команды цикла. Рассмотрим сначала понятия стека.

Стек - это модель оперативной памяти (структура данных), где данные запоминаются и хранятся по принципу первый пришел - последний ушел. Аналогом в военном деле является рожок для патронов к автомату.

Пример. Рекурсивная функция вычисления суммы целых чисел от а до b имеет вид

function Summa(a, b : integer) : integer;

begin

if a = b then Summa := a        {Это стоп-условие рекурсии}

else Summa := b + Summa(a, b-1)  (Это неявный цикл}

end;

Вычислим функцию Summa(3, 5). Формально можно записать

Summa(3, 5) = 5 + Summa(3, 4) = 5 + 4 + Summa(3, 3) = 5 + 4 + 3.

Система выполняет такие вычисления за два этапа: на первом этапе формирует стек, куда заносит числа 5, 4, 3. На втором этапе числа прибавляет в обратной последовательности (поскольку они поступают из стека): 3 + 4 + 5 = 12.

Задача 3. Составить рекурсивную функцию Factorial для вычисления факториала числа n (n! = 1- 2- 3- ...• п, 0! = 1, 1! = 1), которая основывается на многоразовом (рекурсивном) использовании формулыn! = n (n— 1)!

function Factorial(n : integer) : integer;

begin

if n = 0 then Factorial := 1     {Это стоп-условие)

else Factorial := n * Factorial(n-l)

end;

Вычислим 4!:

Factorial(4) = 4 • Factorial(3) = 4 •  3 •  Factorial(2) =

= 4 •  3 •  2 •  Faotorial(l) = 4 •  3 •  2 •  1 •  Factorial(O) =

= 4 •  3 •  2 •  1 •   1.

В стек будут занесены числа 4, 3, 2, 1, 1. Следовательно получим результат: 1 • 1 • 2 • 3 • 4 = 24.

Замечание. Применяя рекурсию, нужно правильно составлять стоп-условия, которые обеспечивают окончание рекурсивных вычислений.

Задание 3. Составить программу решения задачи № 6, исполь­зуя рекурсивные функции.

5. Открытые массивы. В списках формальных параметров подпрограммы можно описывать открытые массивы - массивы заранее неизвестного размера. Их описание имеет вид:

<имя массива> : array of <имя базового типа>;

Нумерация элементов такого формального массива в подпрограмме всегда начинается с нуля. Номер последнего элемента можно определить с помощью стандартной функции

high(<имя массива>).

Открытый массив используют для поочерёдной обработки в процедуре массивов различных размеров.

Задача 4. Используя подпрограммы, создать массив у, элементы которого заданы формулой ут = fy(m) = random(m), т =1, 2, ..., 7, и массив g с элементами gn=fg(n)=n2/2,

п = 1, 2, ..., 9. В каждом массиве определить количество элементов, больших 4. Вывести на экран результаты вычислений.

program MyProcedure;

{$F+}

uses Crt;

type        myfunc = function(n : integer) : real;

var         у : array [1..7] of real;

   g : array [1..9] of real;

function fy(m : integer) : real;

begin

fy := random(m)

end;

function fg(n : integer) : real;

begin

fg := n * n / 2

end;

procedure Sozdat(f : myfunc; var z : array of real);

var і : integer;

begin

for і := 0 to high(z) do

begin

z[i] := f(i + 1);

write(z[i]:5:2);

end;

writeln

end;

function kol(z : array of real) : integer;

var i, k : integer;

begin

k := 0;

for і := 0 to high(z) do

if z[i] > 4 then k := k + 1;

kol := k

end;

begin

clrscr;

randomize;

Sozdat(fy, y);

Sozdat(fg, g);

write('Количество искомых элементов .у - ');

writeln('k = ', kol(y):3);

write(' Количество искомых элементов  g - ');

writeln('k = ', kol(g):3);

readln

end.

Замечание. Обратите внимание на использование впервые типа данных функция (function):

type myfunc = function(n : integer) : real.

К этому типу отнесены конкретные функции fy(x) и fg(x). Благодаря типу myfunc можно, используя всего одну процедуру, создавать различные массивы. В связи с этим в программу включена директива {$F+}, которая поддерживает необходимую модель (far-модель) вызова подпрограмм.

Задание 4. Решите задачу № 16 своего варианта.

6. Стандартные модули. Подпрограммы, которые имеют универсальное назначение и могут пригодиться многим пользователям, следует объединять в библиотеки и модули.

Модульпрограммная единица. Он содержит описание констант, типов данных, переменных и подпрограмм. Различают стандартные модули и модули, созданные пользователем.

Существуют такие стандартные модули:

System   -  содержит часто используемые процедуры и функции;

String    - содержит функции для работы со строковыми переменными;

Printer   — модуль для работы с устройством печати;

Graph     - содержит процедуры и функции для графических построений;

Overlay - модуль для работы с большими программами;

Dos, Windows - дают возможность выполнять команды операционной системы во время выполнения Паскаль-программ или получать определённые данные от операционной системы;

Graph3, TurboS - обеспечивают совместимость с предыдущими версиями ТР и т.д.

Подсоединение модулей в конкретной программе осуществля ется с помощью команды

uses <список имён модулей>;

Процедуры и функции из модуля System используются по умолчанию. Именно из этого модуля компилятор берёт процедуры read, readln, write, writeln, стандартные функции sin, cos и т.д.

Рассмотрим несколько полезных процедур, которые принадлежат модулям System и Crt:

exit                             - служит для выхода из текущей подпрограммы или остановки работы основной программы;

halt                             - останавливает выполнение программы и передаёт управление операционной системе;

break              - служит для принудительного выхода из циклов for, while или repeat;

continue         - предназначена для перехода к выполнению следующей итерации в циклах for, while, или repeat;

delay(n)          - приостанавливает выполнение следующей команды на заданное пользователем время (в микросекундах);

clrscr              - очищает экран;

textcolor(цвет) - задаёт цвет текста (число от 0 до 15);

textbackground(цвет) - задаёт цвет фона экрана.

Среди функций модуля Crt часто используют символьную функцию readkey, которая приобретает значение нажатого пользователем символа на основной символьно-цифровой части клавиатуры, а также логическую функцию keypressed, которая получает значение true, если пользователь нажмёт любую клавишу на основной клавиатуре.

В модуле Dos определены процедуры и функции для работы с файловой системой в режиме выполнения паскаль-программы. Для выполнения exe-файла некоторой программы изнутри текущей программы используют процедуру

ехес('<полное имя ехе-файла>',

‘<параметры программы>’ или ‘’).

Замечание. В начале программы, где используется процедура ехес, необходимо включить директиву М, например, с такими параметрами {$М $2000,0,1000}.

Для хронометрирования времени выполнения программы или её фрагментов, а также для обработки даты используют такие две процедуры:

GetTime(Hour, Minute, Second, SotiSec) — присваивает указанным в списке переменным числовые значения текущего времени (час, мин, сек, сотые сек);

GetDate(Year, Month, Day, Number) — присваивает переменным из списка типа word значение текущей даты (год, месяц, день, день недели).

Задание 5. Рассмотрите задание № 3 о вычислении тремя способами знакопеременной суммы с точностью е = 0,00001 (см. § 5, п. 2, задачу-образец № 4, задачу № 7 из сборника "Задачи"). Создайте ехе-файлы для соответствующих трёх программ. Напишите программу, которая будет состоять из директивы {$М...}, команды uses Crt, Dos; трёх, команд типа exec('summal.exe', ‘’); нескольких команд gettime(...), размещённых так, чтобы измерять время выполнения процессором каждой из подсоединённых подпрограмм. Определите время в микросекундах, затраченное процессором для решения задачи каждым способом. Каким способом результаты были получены быстрее?

7. Модули пользователя (unit). Собственный модуль пользователь составляют по определенным правилам. Структура модуля такая:

unit <имя>;

interface                                {интерфейсный блок}

 

               <раздел описаний>

implementation                     {блок реализации заданий}

               <тексты подпрограмм пользователя>

begin

               <блок инициализации>

end.

Пользователь дает модулю имя. Оно должно быть уникальным. В разделе описаний можно объявлять другие модули, описывать типы, константы и заголовки подпрограмм, доступные в данном модуле.

В блоке реализации записывают тексты подпрограмм в порядке упоминания их заголовков в описаниях. Списки параметров можно не писать, поскольку они приведены в описаниях заголовков. Блоки интерфейса и реализации могут быть пустыми. Такую возможность используют при необходимости описать, например, общие для многих программ постоянные, переменные, типы данных.

В блоке инициализации можно задавать исходные данные, открывать файлы и т.д. Этот блок выполняется первым, то есть перед командами основного блока главной программы, к которой присоединён данный модуль. Если этот блок не используется, то служебное слово begin не пишут.

Пример. Создадим модуль, который определяет функции tg(x), хУ и чистит экран.

unit Mymodul;

interface

uses Crt;

function tg(x : real) : real;

function step(x, у : real) : real;

implementation

function tg(x : real) : real;

begin

tg := sin(x) / cos(x) end;

function step(x, у : real) : real;

begin

step := exp(y * ln(x))

end;

begin

clrscr

end.

Для оформления подпрограммы в виде модуля нужно в меню среды Турбо Паскаль в пункте Compile/Destination установить значение Disk и оттранслировать ее (Ctrl + F9). На диске получим файл с тем же именем, но с расширением .tpu. Модуль создан.

Пример. Вычислим tg(x) и 1.35, используя модуль Mymodul.

program UseModul;

uses Mymodul;

var x, y, a, b, с : real;

begin

a:= 1.3;

b := 5;

writeln('Введите х:');

readln(x);

у := tg(x);

writeln(‘tg(‘, x:5:2,’) = ‘, y:6:2);

с := step(a, b);

writeln(‘1.3^5 = ', c:5:2);

readln

end.

Задание 6.  Модифицируйте модуль и программу так, чтобы можно было вычислить значение функции ctg3x - 4tgx.

Отдельные модули пользователь может объединять в личную библиотеку (например, Mybibl.tpl) с помощью команды

tpuMover <полный путь к файлу>\Mybibl.tpl /+ Mymodul

Замечание. Файл Mybibl.tpl необходимо разместить в каталоге, где нет файла с системной библиотекой turbo.tpl.

Задание 7. Решите задачу № 17 своего варианта.

 

 

16