За какво се използват подпрограмите? Концепцията за подпрограма

В практиката на програмиране често възникват ситуации, когато една и съща група оператори, изпълняващи определена цел, трябва да се повтори без промени на няколко места в програмата. За да се отървем от такава загуба на време, беше предложена концепцията за подпрограма.

Подпрограмата е именувана, логически пълна група от езикови изрази, които могат да бъдат извикани за изпълнение произволен брой пъти от различни места в програмата. Във Free Pascal има два вида рутинни процедури: процедури и функции. Основната разлика между процедура и функция е, че резултатът от изпълнението на операторите, които съставляват тялото на функцията, винаги е определена стойност, така че функцията може да се използва директно в изрази, заедно с променливи и константи.

4.1 Обща информация за подпрограмите. Локални и глобални променливи

И така, подпрограмата е именуван набор от декларации и изрази, които изпълняват конкретна задача. Информацията, предадена на подпрограмата за обработка, се нарича параметри, а резултатът от изчисленията се нарича стойности. Извикването на подпрограма се нарича повикване. Преди да извикате, подпрограмата трябва да бъде описана в раздела с описания. Описанието на подпрограмата се състои от заглавка и тяло. Заглавката декларира името на подпрограмата и нейните параметри, ако има такива, в скоби. За функция трябва да посочите вида на резултата, който връща. Тялото на подпрограмата следва заглавката и се състои от декларации и изпълними оператори.

Всяка подпрограма може да съдържа описания на други подпрограми. Константи, променливи, типове данни могат да бъдат декларирани както в основната програма, така и в подпрограми с различна степен на вложеност. Променливите, константите и типовете, декларирани в основната програма преди дефинирането на подпрограми, се наричат ​​глобални и са достъпни за всички функции и процедури. Променливите, константите и типовете, декларирани в подпрограма, са достъпни само в тази подпрограма и се наричат ​​локални.

За да определите правилно обхвата на идентификаторите (променливите), трябва да се придържате към следните правила:

  • всяка променлива, константа или тип трябва да бъдат декларирани преди употреба;
  • обхватът на променлива, константа или тип е подпрограмата, в която е декларирана;
  • всички имена в рамките на подпрограмата, в която са декларирани, трябва да бъдат уникални и не трябва да съвпадат с името на самата подпрограма;
  • локалните и глобалните променливи с едно и също име са различни променливи; достъпът до такива променливи в подпрограма се третира като достъп до локални променливи (глобалните променливи не са налични);
  • При достъп до подпрограма са достъпни обекти, които са декларирани в нея и преди нейното описание.

4.2 Формални и действителни параметри. Предаване на параметри към подпрограма

Обмен на информациямежду извиканите и извикващите функции се осъществява с помощта на механизма за предаване на параметри. Променливите, изброени в заглавката на функцията, се наричат ​​формални параметри или просто параметри на подпрограмата. Всички променливи от този списък могат да се използват в рамките на подпрограма. Списъкът с променливи в израза за извикване на подпрограма са действителните параметри или аргументи.

Механизмът за предаване на параметри осигурява обмен на данни между формални и действителни параметри, което ви позволява да изпълнявате подпрограма с различни данни. Има съответствие едно към едно между действителните параметри в оператора за повикване и формалните параметри в заглавката на подпрограмата. Броят, видовете и редът на формалните и действителните параметри трябва да съвпадат.

Предаващи параметрисе извършва по следния начин. Изчисляват се изразите, които заемат мястото на действителните параметри. Паметта се разпределя за формалните параметри според техните типове. Извършва се проверка на типа и ако не съвпадат, a диагностично съобщение. Ако броят и видовете формални и действителни параметри съвпадат, тогава механизмът за прехвърляне на данни между действителните и формалните параметри започва да работи.

Формалните параметри на процедурата могат да бъдат разделени на два класа: стойностни параметри и променливи параметри.

При предаване на данни през стойностни параметри, стойностите на действителните параметри се предават на подпрограмата и няма достъп до самите действителни параметри от подпрограмата. При предаване на данни параметрите-променливи заместват 1 В действителност адресите на действителните параметри се предават на подпрограмата.формални параметри и следователно подпрограмата има достъп до стойностите на действителните параметри. Всяка промяна в променливите параметри в подпрограмата води до промяна в съответните формални параметри. Следователно входните данни трябва да се предават през стойностни параметри; променливите параметри трябва да се използват за предаване на данни, променени в резултат на операцията на подпрограмата.

Нека да преминем от общите теоретични принципи към практическото използване на подпрограми при решаване на проблеми. Нека започнем да изучаваме подпрограмите с процедури.

4.3 Процедури

Описанието на процедурата е както следва:

процедура име_на_процедура(списък_с_формални_параметри); етикет списък_етикети; const списък_константи; тип type_list; var variable_list; начало //Тяло на процедурата. край;

Описанието започва със заглавието на процедурата, където процедура е ключовата дума на езика, име_на_процедура е всеки идентификатор, разрешен в езика Free Pasacal, списък_с_формални_параметри- имена на формални параметри и техните типове, разделени с точка и запетая. Нека да разгледаме примери за заглавки на процедури с параметри на стойност:

процедураиме_1(r: истински; аз: цяло число; ° С: въглен);

Параметри от един и същи тип могат да бъдат изброени разделени със запетаи:

процедураиме_2(a, b: истински; i, j, k: цяло число);

Списъкът с формални параметри не е задължителен и може да липсва:

процедураиме_3;

Ако променливите параметри ще бъдат използвани в заглавката на процедурата, тогава служебната дума var трябва да бъде посочена преди тях:

процедураиме_4(x, y: истински; вар z: истински);

//x, y - параметри на стойността,

//z - параметър - променлива.

След заглавката идва тялото на процедурата, което се състои от описателна секция 2 Разделът за описание в процедура може да бъде пропуснат, ако не е необходим.(константи, типове, променливи, процедури и функции, използвани в процедура) и езикови оператори, които изпълняват алгоритъма на процедурата.

За достъп до процедура трябва да използвате телефонния оператор:

име_на_процедура(действителен_списък_параметри);

Действителните параметри в списъка на операторите за повикване са разделени със запетая:

а: = 5,3; k: = 2; s:= ’a’;

име_1(a, k, s);

Ако в описанието на процедурата не е имало формални параметри, тогава не трябва да има такива при извикването им:

ЗАДАЧА 4.1. Намерете реалните корени на квадратно уравнение.

Алгоритъмът за решаване на тази задача е описан подробно в задача 3.3 (фиг. 3.14). Ситуацията с неправилно въвеждане на стойности на коефициента обаче не беше разгледана там. Например, ако потребителят въведе, уравнението ще се промени от квадратно на линейно. Алгоритъмът за решаване на линейно уравнение е тривиален: , при условие че . За да не усложняваме вече компилирания алгоритъм за решаване на квадратно уравнение, го записваме под формата на подпрограма-процедура. Следва фрагмент от програмата с коментари:

//Процедура за изчисляване на реалните //корени на квадратно уравнение. процедура corni (a, b, c: real; var x1, x2: real; var pr: boolean); //Входни параметри на процедурата: //a,b,c - коефициенти на квадратното уравнение; //Изходни параметри на процедурата: //x1,x2 са корените на квадратното уравнение, //pr е логическа променлива, //приема стойността "false", ако в уравнението няма корени, //и стойност "true" в противен случай. var d: истински; начало d:=b * b-4 * a * c; ако d<0 then pr:= false else begin pr:= true; x1:=(-b+sqrt (d)) / 2 / a; x2:=(-b-sqrt (d)) / (2 * a); end end; //Конец подпрограммы //Основная программа var a_, b_, c_, x1_, x2_, x_ : real; pr_ : boolean; begin write (’a_:= ’); readln (a_); write (’b_:= ’); readln (b_); write (’c_:= ’); readln (c_); if a_=0 then //Если а=0, то уравнение //квадратным не является. begin //Решение линейного уравнения bx+c=0. if b_<>0 след това започнете x_:=-c_/b_; writeln('x=',x_); end else writeln('Няма корени'); end else //Решаване на квадратното уравнение ax^2 + bx + c = 0. begin korni (a_, b_, c_, x1_, x2_, pr_); //Извикване на процедурата. if pr_=false then writeln ('Без корени') else writeln (' x1= ',x1_, ' _x2= ',x2_); край; край.

ЗАДАЧА 4.2. Въведете поредица от положителни цели числа. Намерете най-голямата и най-малката цифра във всяко число.

За да решим проблема, ще създадем процедура max_min, резултатът от която ще бъде две стойности: минималната и максималната цифра в дадено число.

Текст на програмата:

//Процедурата връща //max най-големите и min най-малките цифри в числото M. //В списъка с параметри: //M параметър-стойност (входен параметър), //max и min параметри-променливи (изходни параметри ). процедура max_min(M: longint; var max: byte; var min: byte); var i: байт; начало i: = 1; докато M div 10>0 do begin if i =1 then begin //Предполагаме, че първата цифра е max:=M mod 10; //най-голям или min:=M mod 10; //най-малък. i:= i +1; край; //Търсене на числа, по-големи от max или по-малки от min. ако M mod 10 > max тогава max:=M mod 10; ако M mod 10< min then min:=M mod 10; M:=M div 10; end; end; var X: longint; N, i,X_max, X_min: byte; begin //Количество элементов в последовательности. write (’N= ’); readln (N); for i:=1 to N do begin write (’X= ’); readln (X); //Элемент последовательности. if X>0 then //Ако елементът е положителен, започва max_min(X,X_max, X_min); //извикване на процедурата. //Отпечатайте резултатите. writeln(’ max= ’,X_max, ’ min= ’,X_min); край; край; край.

В урока ще бъде обяснен алгоритъмът за работа с процедури в Pascal. Ще бъдат разгледани примери за използване на процедура с и без параметри. Запознайте се с понятията: формални и действителни параметри, параметър-променлива и параметър-стойност


Подпрограмае част от код, която има собствено име и се създава, ако е необходимо този код да се изпълни няколко (много) пъти. Подпрограмата е описана веднъж преди стартиране на основната програма (before begin). Компилаторът пропуска тази част от кода, докато не срещне „извикване“ на подпрограма в главната програма, което изглежда като извикване по име (може би име с аргументи, посочени в скоби).

В много езици за програмиране подпрограмите съществуват само като функции. въпреки това в Pascal подпрограмата е едновременно функция и процедура. Разликата между двете ще стане очевидна в този урок.

Така че нека помислим синтаксис за деклариране и описание на процедура в Pascal

var ...; (област за деклариране на глобална променлива) име на процедура (параметри); (начало на процедурата) var ...;(декларация на локални променливи) начало ... (тяло на процедурата) край; (край на процедурата) начало ... (главна програма) край.

Пример:Процедура без параметри, която отпечатва 60 звездички, всяка на нов ред

процедура pr; var i:integer; begin for i:=1 to 60 do begin (тяло на подпрограмата) write("*"); writeln; край; край; (край на подпрограмата) begin pr; (извикване на процедура) край.

В този пример за работа с процедура в Pascal е очевидно, че компилаторът ще пропусне блока за описание на процедурата и ще стигне до основната програма (ред 9 от кода). И едва след като се срещне извикването на процедурата (ред 10), компилаторът ще продължи да я изпълнява, връщайки се към ред 1.

Процедури с параметри. Фактически и формални параметри

Нека да разгледаме пример за необходимостта от използване на процедура.

Пример:
Изградете фигура

особеност:Три подобни фигури.

  • общ:размери, ъгъл на завъртане
  • разлики:координати, цвят
  • Алгоритъм за решение:

    • подчертайте идентични или подобни действия (три цифри);
    • намери в тях общ(размери, форма, ъгъл на завъртане) и различия(координати, цвят);
    • запишете разликите под формата на неизвестни променливи, те ще го направят параметрипроцедури.

    Решение в Pascal:
    Процедура:

    програма:

    15 използва GraphABC; процедура Tr( x, y: цяло число; цвят: система. Чертеж. Цвят) ; начало MoveTo(x, y); LineTo(x, y- 60) ; LineTo(x+ 100 , y) ; LineTo(x, y); FloodFill(x+ 20, y- 20, цвят); край ; начало SetPenColor(clBlack) ; Tr(100 , 100 , clBlue) ; Tr(200 , 100 , clGreen) ; Tr(200 , 160 , clRed) ; край.

    използва GraphABC; процедура Tr(x, y: цяло число; цвят:система.Чертеж.Цвят); начало Преместване към (x, y); LineTo(x, y-60); LineTo(x+100, y); LineTo(x, y); FloodFill(x+20, y-20,цвят); край; започнете SetPenColor(clBlack); Tr(100, 100, clBlue); Tr(200, 100, clGreen); Tr(200, 160, clRed); край.

    Нека да разгледаме синтаксиса на декларацията и описанието на процедурата с параметрив Паскал.

    var ...;(област за деклариране на глобална променлива) procedure pr(параметър1, параметър2: цяло число; параметър3:char); (начало на процедурата) var ...;(декларация на локални променливи) begin ... (тяло на процедурата) end; (край на процедурата) begin ... (главна програма) pr (параметър1, параметър2, параметър3 ); (извикване на процедура) край.

    Процедура 1: нвертикални линии. нсе определя от параметър на процедурата.


    Процедура 2:Напишете процедура за рисуване нкръгове, изместени хоризонтално. н, радиус Ри отстъп Осе определят от параметрите на процедурата (общо 3 параметъра).

    Пример:Напишете процедура, която отпечатва посочения знак (въведен от клавиатурата) 60 пъти, всеки на нов ред

    Параметри на процедурата(на някои езици те се наричат ​​аргументи) се посочват в скоби след името му (в декларацията).

    В този пример ще използваме параметъра на процедурата като въведен знак. Параметър на формалната процедурапосочено в скоби при описанието му. Необходимо е да се посочи типът на формалния параметър, разделен с двоеточие.

    Действителен параметъре стойността, която е посочена в скоби при извикване на процедурата. Действителният параметър може да бъде конкретна стойност (буквал: число, символ, низ...) или променлива, която компилаторът ще замести на мястото на официалния параметър. Следователно типът данни на формалните и действителните параметри на процедурата трябва да бъде еднакъв.

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 var s: char; процедура pr(a: char); (a е формален параметър) var i: цяло число; begin for i: = 1 до 60 do begin write (a) ; writeln ; край ; край ; започнете writeln("символ"); readln(s); pr(s); (s е действителният параметър)край.

    var s:char; процедура pr(a:char); (a е формален параметър) var i:integer; begin for i:=1 to 60 do begin write(a); writeln; край; край; започнете writeln("символ"); readln(s); pr(s); (s е действителният параметър) край.

    В този пример, когато извиква процедурата, компилаторът ще замени формалния параметър a с действителния параметър s, т.е. знака, който ще бъде въведен от клавиатурата. И двата параметъра са от тип данни char.

    Процедура на задачата 3.Напишете процедура, която събира произволни две числа (два параметъра).

    Процедури с параметри. Променлив параметър

    1. начин:
    2. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 променлива x, y, m, n: цяло число; процедура MaxNumber(a, b: цяло число; var max: цяло число) ; (a и b са стойностни параметри, max е променлив параметър) start if a>b then max: = a else max: = b; край ; започнете запис ("введете x,y" ); readln(x, y); MaxNumber(x, y, m) ; (действителни параметри) writeln ("max=", m) край.

      променлива x,y,m,n:цяло число; процедура MaxNumber(a,b:integer;var max:integer); (a и b са стойностни параметри, max е променлив параметър) begin if a>b then max:=a else max:=b; край; започнете запис ("въведете x,y"); readln(x,y); Макс.Число(x,y,m); (действителни параметри) writeln("max=",m) край.

      В примера формалните параметри a и b се използват за съхраняване на сравняваните числа, а променливият параметър max се използва за съхраняване на максимума от двете числа. Променлив параметърили изходният параметър предава стойността си на главната програма (действителния параметър m), т.е. връща стойност, докато formal параметри-стойности(въведен параметър), напротив, вземете стойности от основната програма (от действителните параметри x и y). За променлив параметър (max) се използват онези клетки от паметта, които са заделени за съответния параметър при извикване на процедурата (клетка m).

      И така, нека формулираме понятията:

      Ако обикновена променлива, указваща нейния тип, е посочена като формален параметър, тогава такъв параметър е такъв параметър-стойностили входен параметър(a и b в примера). Типът данни на формален стойностен параметър трябва да съответства на типа данни на неговия действителен параметър (a и b трябва да съответстват на типовете данни на x и y по двойки).

      Ако името на формален параметър в декларация на процедура е предшествано от функционална дума вар, тогава този параметър се извиква променлив параметърили в почивните днипараметър (макс. в примера). Той използва онези клетки от паметта, които са заделени за съответния параметър при извикване на процедурата (m). Действителният параметър, съответстващ на променлив параметър, може да бъде само променлива (не константа, не литерал или израз).

    3. начин:
    4. Използването на променливи параметри ви позволява да губите по-малко памет

      1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 променлива x, y: цяло число; процедура exchange(a: integer; var b: integer) ; (b - променлив параметър или изходен параметър) var c: цяло число; start if a>b then begin c: = a; a: = b; b: = c; (вторият параметър на процедурата - b - винаги ще бъде максимален)край ; край ; започнете да пишете ( "въведете две числа") ; readln(x, y); обмен (x, y) ; writeln ("max=", y) край.

      var x,y:integer; процедура exchange(a: integer;var b:integer); (b - променлив параметър или изходен параметър) var c:integer; start if a>b then begin c:=a; a:=b; b:=c; (вторият параметър на процедурата - b - винаги ще бъде максимумът) end; край; begin writeln("въведете две числа"); readln(x,y); обмен (x,y); writeln("max=",y) край.

      Използвайки този метод за решаване на проблема, направихме без третия параметър. За да направим това, използвахме друга локална променлива c в процедурата. Процедурата променя стойностите на променливите a и b, така че b винаги да е максимумът. Следователно в ред 15 на програмата вторият параметър (y), съответстващ на формалния параметър b, се показва като максимум.

    Процедура на задачата 4.

    1. Трябва да определите най-големия общ делител на две въведени числа с помощта на цикъл.
    2. Необходимо е да се определи най-големият общ делител на две въведени числа чрез процедурата (два стойностни параметъра, един променлив параметър).


    Вербален алгоритъм:

    • Въведено аИ b(Например, 18 И 24 )
    • Повтаряме следните стъпки в цикъл:
    • Ако А < b, след това разменяме променливите (стъпка 1: а=24, b=18; Стъпка 2: а=18, b=6)
    • Променлива азадайте остатъка от делението аНа b(1 стъпка: а=6, b=18; Стъпка 2: а=0, b=6)
    • Когато остатъкът е 0 , резултатът се показва (променливи стойности b) (b=6)

    Алгоритъм за решаване на GCD търсене:

    Процедура на задача 5.Използвайки процедурите, изградете фигура:


    Процедура на задача 6.Дадени са 3 различни масива от цели числа (всеки размер е 15 елемента). Във всеки масив намерете средната аритметична стойност.
    За да формирате елементи на масива и да изчислите сумата и средното аритметично, използвайте една процедура (средното аритметично и сумата са формализирани като променливи параметри).

    В задачите на Pascal често има нужда да се попълни масив с данни и след това да се покажат стойностите на екрана. Защо не автоматизирате тази задача за попълване и извеждане на масива - т.е. формализирайте с помощта на процедури и след това използвайте тези процедури, ако е необходимо.

    Пример:Създайте процедура за показване на десет елемента от масив (два параметъра: брой елементи, масив)

    Покажи решение:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const n = 10; var i: цяло число; a, b: масив [1 .. n] от цяло число; процедура arr_out (k: цяло число; arr: масив [1 .. n] от цяло число) ; var i: байт; начало на запис ("изход на масив: " ); for i : = 1 to k do write (arr[ i] : 4 ); writeln ; край ; start for i: = 1 to n do a[ i] : = random(10) ; arr_out (n, a); край.

    const n = 10; var i:integer; a, b: масив от цели числа; процедура arr_out (k:цяло число; arr: масив от цяло число); var i: байт; начало на запис ("изход на масив: "); за i:= 1 до k направи запис (arr[i]:4); writeln; край; започнете за i:=1 до n направете a[i]:=random(10); arr_out(n, a); край. цяло число; процедура arr_rand (k: цяло число; var arr: масив [1 .. n] от цяло число) ; var i: байт; започнете да пишете ( „Попълване на масив със случайни числа“) ; рандомизирам; for i : = 1 to k do arr[ i] : = random(100 ) ; край ; начало arr_rand (n, a); край.

    const n = 10; var a, b: масив от цели числа; процедура arr_rand (k:цяло число; var arr: масив от цяло число); var i: байт; begin write("Попълване на масива с произволни числа"); рандомизирам; за i:= 1 до k направете arr[i]:=random(100); край; начало arr_rand(n, a); край.

    Процедура на задача 7.Комбинирайте двете решени задачи (за попълване и извеждане на масив).


    Процедура на задачата 8.Добавете процедура към задачата за попълване на стойностите на масива от потребителя (въвеждане на стойности от клавиатурата). Изведете два различни масива: единият с произволно генерирани стойности, другият с въведени от потребителя стойности.


    Процедура на задача 9.Създаване на програма с процедура за изчисление (входни параметри: число и степен). За да покажете резултата, използвайте променлив параметър.

    Самостоятелна работа

    Опция 1:за 5 едномерни масива, определя произведението на елементите на всеки масив с помощта на процедура с два параметъра - брой елементи на масива и променлив параметър за извеждане на произведението.

    Вариант 2:за 5 едномерни масива, определете минималния елемент на всеки масив, като използвате процедура с два параметъра - брой елементи на масива и променлив параметър за показване на минималния елемент.

    * комплексС помощта на процедурата произволно генерирайте едномерни масиви от 10 елемента (стойности от -20 до +20). Извикайте процедурата, докато сред стойностите се появи нула.

    Когато решавате нови задачи, можете да опитате да използвате предварително написани програми. Алгоритъм, разработен преди това и използван изцяло като част от други алгоритми, се нарича спомагателен. Използването на спомагателни алгоритми ви позволява да разделите проблема на части и да го структурирате.

    Цялата програма може да бъде разделена на две части: основна и спомагателна. В основната част се извършва най-простата обработка на информация, организира се достъп до различни спомагателни модули (подпрограми).

    Един спомагателен алгоритъм може да извиква и други спомагателни алгоритми; дължината на такава верига от повиквания е теоретично неограничена. Тук и по-долу като синоними се използват следните двойки думи: алгоритъм и програма, спомагателен алгоритъм и подпрограма, команда и оператор, програма и модул. Помощните и главните алгоритми не са сами по себе си, а във връзка един с друг.

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

    Подпрограмите могат да бъдат два вида: подпрограма без параметри и подпрограма с параметри. Една подпрограма може да бъде извикана от всяко място в основната програма или друга подпрограма толкова пъти, колкото желаете.

    Когато работите с подпрограми, концепциите за формални и действителни параметри са важни. Формалните параметри са входни идентификатори за рутина. Ако формалните параметри получават конкретни стойности, те се наричат ​​действителни. Формалните параметри могат да получават конкретни стойности само в програмата, където се осъществява достъп до дадения подпрограмен модул. Типът и редът на записване на действителните параметри трябва да бъдат същите като формалните параметри. В противен случай резултатът от програмата ще бъде непредсказуем. От това следва, че действителните параметри се използват при достъп до подпрограма от основната, а формалните параметри се използват само в самия модул.

    Подпрограма с параметри се използва за запис на многократно повтарящи се действия с различни начални данни. Подпрограмите с параметри могат да бъдат разделени на два вида: подпрограми-функции и просто подпрограми с параметри (те се наричат ​​процедури).

    При съставянето на подпрограми с параметри трябва да се спазват следните правила:

    1) всяка подпрограма има собствено име и списък с формални параметри;

    2) процедурата от главната програма се извиква чрез команда за извикване, която по форма не се различава от извикване на команда изпълнител. Резултатът се присвоява на една или повече променливи, които са в списъка с формални параметри. Но резултатът, разбира се, може да бъде не само стойността на променливите, но и някакво действие, извършено от компютъра.

    Пример 1. Използваме алгоритъма за намиране на най-голям общ делител на две естествени числа като спомагателен при решаването на задачата: създаваме програма за изваждане на дроби (a, b, c, d са естествени числа). Представете резултата като обикновена несъкратима дроб.

    Подпрограма.

    1) Въведете естествените числа M, N.

    2) Ако M=N, преминете към стъпка 5, в противен случай преминете към следващата точка.

    3) Ако M>N, тогава M:=M-N, в противен случай N:=N-M.

    4) Отидете на стъпка 2.

    5) Предайте стойността на M на основната програма.

    6) Край на подпрограмата.

    Основна програма.

    1) Въведете стойностите A, B, C, D.

    2) E:=A*D - B*C.

    4) Ако E=0, изведете стойността на E и преминете към стъпка 9, в противен случай преминете към следващата стъпка.

    5) M:=|E|, N:=F, отидете на подпрограмата за изчисляване на GCD.

    7) Разделете E и F напълно на G.

    8) Отпечатайте стойностите на E и F.

    9) Край на програмата.

    Var A, B, C, D, G, E, F: Цяло число;

    Процедура Nod(M, N: Integer; Var K: Integer);

    Докато М<>N Направи

    Ако M >

    Write("Въведете числителите и знаменателите на дробите:");

    ReadLn(A, B, C, D);

    E:= A * D - B * C;

    Ако E = 0, тогава WriteLn(E)

    Кимване (Abs(E), F, G);

    WriteLn("Отговор: ", E, "/", F)

    Както се вижда от примера, декларацията и тялото на подпрограмите се намират в секцията с описания. Заглавието на подпрограмата съдържа списък с формални параметри, указващи техния тип, които могат да бъдат разделени на входни и изходни параметри (предшествани от услугата Var). Когато се извика процедура, се посочва нейното име и списък с действителни параметри. Формалните и действителните параметри трябва да съответстват по количество и вид.

    Процедурата се нарича, както следва:

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

    Например,

    Кимване (Abs(E), F, G);

    Според метода за прехвърляне на действителни стойности към подпрограма в Turbo Pascal 7.0 има променливи параметри, параметри на стойността, постоянни параметри и масиви от отворен тип, низове от отворен тип, параметри на процедурата, параметри на функция (подробности в литературата) .

    Функцията (за разлика от процедурата) винаги връща една стойност.

    Нека да покажем как се променя подпрограмата от примера, ако е написана като функция.

    Функция Nod(M, N: Integer) : Integer;

    Докато М<>N Направи

    Ако M > N Тогава M:= M - N Друго N:= N - M;

    Така след списъка с параметри се посочва типът на стойността на функцията, а в тялото на функцията поне веднъж има присвояване на променлива, чието име съвпада с името на функцията към съответната стойност.

    Извикването на функцията ще бъде както следва:

    G:= Кимване (Abs(E), F);

    По принцип извикването на функция може да присъства в израз от дясната страна на оператор за присвояване, в изходна процедура, като действителен параметър в извикване на друга подпрограма и т.н.

    Когато решавате проблеми, препоръчително е да анализирате условието, да напишете решението в големи блокове (които не са Pascal оператори), да детайлизирате всеки от блоковете (написан под формата на блокове, може би все още не Pascal оператори) и т.н., продължете до докато всеки от блоковете не бъде реализиран с помощта на езикови оператори.

    Пример 2. Дадено е естествено число n. Разменете първата и последната цифра на това число.

    Ако е невъзможно (N)

    Тогава WriteLn("Не мога да пренаредя цифрите, ще възникне препълване")

    WriteLn("Отговор: ", N)

    Може да се отбележи, че е необходимо да се уточнят логическата функция Impossible, която диагностицира дали пермутацията е възможна, и процедурата Change, която (ако е възможна) извършва тази пермутация.

    Функция невъзможна (N: цяло число): Булева;

    Ако число (N)< 5

    Тогава Невъзможно:= Невярно

    Друго невъзможно:= (N Mod 10 > 3) Или

    (N Mod 10 = 3) И

    (N Mod 10000 Div 10 * 10 + N Div 10000 > MaxInt Mod 10000)

    Тук е необходимо да се уточни подробно функцията Number, която връща броя на цифрите в естествено число (тъй като функцията Impossible съдържа своето извикване, функцията Number трябва да я предхожда в раздела за описание).

    Номер на функцията (N: Цяло число): Цяло число;

    Var Vsp: Цяло число;

    Докато N > 0 Do

    Vsp:= Vsp + 1; N:=N Раздел 10

    И накрая, последната процедура.

    Промяна на процедурата (променлива N: цяло число);

    Var Kol, P, S, R: Цяло число;

    Кол:= Число(N);

    P:= N Mod 10; (последна цифра)

    Ако Kol > 1 Тогава

    S:= N Div Round(Exp((Kol - 1) * Ln(10)))

    Иначе S:= 0; (първа цифра)

    R:= N Mod Round(Exp((Kol - 1) * Ln(10))) Div 10;

    N:= P * Round(Exp((Kol - 1) * Ln(10))) + R * 10 + S

    Възможни са и подпрограми, които се самоизвикват. Те се наричат ​​рекурсивни. Създаването на такива подпрограми е красива техника за програмиране, но не винаги е препоръчително поради прекомерната консумация на компютърна памет.

    Пример 3. Намерете максималната цифра в записа на дадено естествено число.

    Програма MaxDigit;

    Тип NaturLong = 1..(High(LongInt));

    Функция Максимум (N: LongInt) : Цифра;

    Тогава Максимум:= N

    Иначе, ако N Mod 10 > Максимум (N Div 10)

    След това Максимум:= N mod 10

    Else Maximum:= Максимум (N Div 10)

    Write("Въведете естествено число: ");

    WriteLn("Максималната цифра е ", Максимум(A))

    При създаването на функцията Maximum е използвано следното съображение: ако едно число се състои от една цифра, то това е максимумът, в противен случай, ако последната цифра не е максимумът, тогава трябва да се търси сред останалите цифри на числото. Когато пишете рекурсивен алгоритъм, трябва да внимавате за граничното условие, когато веригата от рекурсивни извиквания се прекъсва и започва нейното обратно „размотаване“. В нашия пример това условие е N< 10.

    Рекурсията се обсъжда по-подробно в следващата статия.

    Когато решавате нови задачи, можете да опитате да използвате предварително написани програми. Извиква се алгоритъм, предварително разработен и използван изцяло като част от други алгоритми спомагателни. Използването на спомагателни алгоритми ви позволява да разделите проблема на части и да го структурирате.

    Цялата програма може да бъде разделена на две части: основна и спомагателна. В основната част се извършва най-простата обработка на информация, достъп до различни спомагателни модули (подпрограми).

    Един спомагателен алгоритъм може да извиква и други спомагателни алгоритми; дължината на такава верига от повиквания е теоретично неограничена. Тук и по-долу като синоними се използват следните двойки думи: алгоритъм и програма, спомагателен алгоритъм и подпрограма, команда и оператор, програма и модул. Помощните и главните алгоритми не са сами по себе си, а във връзка един с друг.

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

    Подпрограмите могат да бъдат два вида: подпрограма без параметри и подпрограма с параметри. Една подпрограма може да бъде извикана от всяко място в основната програма или друга подпрограма толкова пъти, колкото желаете.

    Когато работите с подпрограми, следните понятия са важни: формални и действителни параметри. Формални параметриТова са идентификаторите на входните данни за подпрограмата. Ако формалните параметри получат конкретни стойности, те се извикват действителен. Формалните параметри могат да получават конкретни стойности само в програмата, където се осъществява достъп до дадения подпрограмен модул. ТипИ поръчказаписите за действителните параметри трябва да бъдат същите като тези за формалните параметри. В противен случай резултатът от програмата ще бъде непредсказуем. От това следва, че действителните параметри се използват при достъп до подпрограма от основната, а формалните параметри се използват само в самия модул.

    Подпрограма с параметри се използва за запис на многократно повтарящи се действия с различни начални данни.

    При съставянето на подпрограми с параметри трябва да се спазват следните правила:

    1) всяка подпрограма има собствено име и списък с формални параметри;

    2) процедурата от главната програма се извиква чрез команда за извикване, която по форма не се различава от извикване на команда изпълнител. Резултатът се присвоява на една или повече променливи, които са в списъка с формални параметри. Но резултатът, разбира се, може да бъде не само стойността на променливите, но и някакво действие, извършено от компютъра.

    Пример 1. Използваме алгоритъма за намиране на най-голям общ делител на две естествени числа като спомагателен при решаването на задачата: създайте програма за изваждане на дроби ( а, b, ° С, дестествени числа). Представете резултата като обикновена несъкратима дроб.

    Подпрограма.

    1. Въведете естествените числа M, N.
    2. Ако M=N, преминете към стъпка 5, в противен случай преминете към следващата точка.
    3. Ако M>N, тогава M:=M-N, в противен случай N:=N-M.
    4. Отидете на стъпка 2.
    5. Предайте стойността M на основната програма.
    6. Край на подпрограмата.

    Основна програма.

    1. Въведете стойностите A, B, C, D.
    2. E:=A*D - B*C.
    3. F:=B*D.
    4. Ако E=0, изведете стойността на E и преминете към стъпка 9, в противен случай преминете към следващата стъпка.
    5. M:=|E|, N:=F, отидете на подпрограмата за изчисляване на GCD.
    6. G:= М.
    7. Разделете E и F напълно на G.
    8. Отпечатайте стойностите на E и F.
    9. Край на програмата.

    Както може да се види от примера, декларацията на функционалната подпрограма е в секцията с описания на прототип на функция, а имплементацията е след основната функция основен. Заглавката на подпрограмата съдържа списък с формални параметри, указващи техния тип, които могат да бъдат разделени на входни и изходни параметри (предшествани са от &). По принцип, когато се извиква функция със списък от параметри без &, се използват копия на параметрите във функцията, които се изтриват след изпълнение. Знакът & казва на компилатора, че е необходимо да се използва самата променлива, а не нейно копие. При извикване на функция се посочва нейното име и списък с действителни параметри. Формалните и действителните параметри трябва да съответстват по количество и вид.

    Описанието на функция в C++ е както следва:

    return_value_type();

    Например,

    Void Nod(int e, int f, int &k); int f1(float a); дълго f2();

    Функцията винаги връща една стойност. Както можете да видите от пример 1, използвахме типа невалиденкато тип връщане. Тези. посочи на компилатора, че нашата функция не връща никаква стойност.

    Нека покажем как подпрограмата от примера ще се промени, ако е написана като функция, която връща самата GCD стойност (без да използва променливата за връщане).

    Int Nod(int m, int n) ( while (m!=n) if (m > n) m -=n; else n -= m; return (n); )

    И така, в тялото на функцията има поне една команда за връщане, която указва каква стойност да се върне като стойност на функцията.

    Извикването на функцията в основната ще бъде както следва:

    G = Nod(fabs(e), f);

    По принцип извикването на функция може да присъства в израз: от дясната страна на операция за присвояване, в изходен оператор, като действителен параметър в извикване на друга подпрограма и т.н.

    Когато решавате задачи, препоръчително е да анализирате условието, да напишете решението в големи блокове (които не са C++ изрази), да детайлизирате всеки от блоковете (написани под формата на блокове, може би все още не C++ изрази) и т.н., продължете до докато всеки от блоковете не бъде реализиран с помощта на езикови оператори.

    Пример 2. Дадено е естествено число н. Разменете първата и последната цифра на това число.

    Тук е необходимо да се уточни подробно функцията Number, която връща броя на цифрите в представянето на естествено число (тъй като функцията Impossible съдържа своето извикване, функцията Number трябва да я предхожда в раздела за описание на прототипа).

    Възможни са и подпрограми, които се самоизвикват. Те се наричат рекурсивен. Създаването на такива подпрограми е красива техника за програмиране, но не винаги е препоръчително поради прекомерната консумация на компютърна памет.

    Пример 3. Намерете максималната цифра в записа на дадено естествено число.

    При създаването на функцията Maximum е използвано следното съображение: ако едно число се състои от една цифра, то това е максимумът, в противен случай, ако последната цифра не е максимумът, тогава трябва да се търси сред останалите цифри на числото. Когато пишете рекурсивен алгоритъм, трябва да внимавате за граничното условие, когато веригата от рекурсивни извиквания се прекъсва и започва нейното обратно „размотаване“. В нашия пример това условие н

    Рекурсията се обсъжда по-подробно в следващата статия.

    Тестови въпроси и задачи
    1. Кои алгоритми се наричат ​​спомагателни алгоритми?
    2. Колко спомагателни алгоритъма могат да присъстват в основния алгоритъм?
    3. Могат ли спомагателни алгоритми, написани за решаване на този проблем, да се използват за решаване на други проблеми, където използването им би било подходящо?
    4. Какви параметри се наричат ​​формални? фактически?
    5. Какво съответствие трябва да се спазва между формалните и действителните параметри?
    6. Може ли да има повече действителни параметри на процедура (функция) от формални? Ами по-малко?
    7. Има ли подпрограми без параметри?
    8. Има ли някакви ограничения за броя на параметрите на подпрограмата? Ако не, тогава как този брой е ограничен в C++?
    9. В кой раздел са декларирани и реализирани подпрограми в C++?
    10. Какъв тип може да бъде стойността на функцията?
    11. Обяснете метода на последователно детайлизиране при разработването на програми.
    12. Какви подпрограми се наричат ​​рекурсивни?
    13. Какво е гранично условие при организиране на рекурсивна подпрограма?

    Глава 17. Команди за извикване на процедури

    Какво е подпрограма

    Подпрограмата е независима компютърна програма, включена в по-сложна програма (по отношение на подпрограмата тя действа като основна или извикваща програма). Можете да извикате подпрограма навсякъде в основната програма (по-често казват „извикване на подпрограма“). В този случай ядрото на процесора трябва да отиде на адреса на първата команда от подпрограмата и след нейното завършване да се върне на адреса на главната програмна команда след командата за извикване на подпрограмата.

    Най-често се изтъкват два аргумента в полза на използването на подпрограми в технологията на програмиране - спестяване на място в RAM и модулност на програмната структура. Подпрограма, в която е кодиран специфичен алгоритъм, може да се извиква многократно, като по този начин спестява място в RAM, но в този смисъл подпрограмата не се различава много от цикъла. По-важното е, че подпрограмите ви позволяват структурно да разделите програма за решаване на сложен проблем на по-малки модули, които решават отделни подзадачи. Тази модулност на структурата на голяма програма значително улеснява нейното развитие.

    Механизмът за поддържане на работа с подпрограми трябва да включва два основни типа команди - команда за извикване на подпрограма и команда за връщане на подпрограма. Командата за извикване на подпрограмата трябва да осигури преход към първата команда на извиканата подпрограма, а командата за връщане трябва да осигури преход към командата на извикващата програма, следваща командата за извикване. Командите принадлежат към категорията команди, които контролират изпълнението на програма.

    На фиг. Фигура 17.1a показва пример за използване на подпрограмата. В този пример има главна програма, която се намира в RAM, започваща от адрес 4000. В главната програма има извикване на подпрограмата PROC1, която се намира в RAM, започваща от адрес 4500. Когато по време на изпълнението на основната програма, ядрото на процесора достигне тази команда, то ще прекъсне изпълнението на основната програма и ще продължи да изпълнява подпрограма PROC1, поставяйки своя начален адрес от 4500 в програмния брояч. Тялото на подпрограмата PROC1 съдържа две команди за извикване на подпрограмата PROC2, която се намира в RAM, започвайки от адрес 4800. След като достигне всяка от тези команди, ядрото на процесора ще спре да изпълнява подпрограмата PROC1 и ще започне да изпълнява подпрограмата PROC2. След като се натъкне на команда RETURN в подпрограма, ядрото на процесора ще се върне към извикващата програма и ще продължи изпълнението си от инструкцията, следваща инструкцията CALL, която е причинила прехода към току-що завършената подпрограма. Този процес е показан схематично на фиг. 17.1. 6.

    Необходимо е да се обърне внимание на следните точки:

    подпрограмата може да бъде извикана от всяко място в други програми
    модули. Може да има произволен брой такива обаждания.

    една подпрограма може да бъде извикана от друга, която от своя страна се извиква от трета. Нарича се гнездене(вложени) повиквания. Дълбочината на вмъкване теоретично може да бъде произволна.

    при връщане от подпрограма трябва да се осигури преход
    точно на командата за извикване, която стартира завършената сесия за изпълнение на подпрограмата.



    Ориз. 17.1. Извикване на вложена подпрограма:

    a - команди за повикване и връщане; b - последователност на изпълнение на командата

    От всичко това следва, че ядрото на процесора, когато изпълнява команда за извикване на подпрограма, трябва по някакъв начин да запази адреса за връщане (т.е. адреса на командата на извикващата програма, следваща командата за извикване, която се изпълнява). Има три места, където можете да запазите адреса си за връщане:

    регистър на процесора;

    начален адрес на подпрограмата;

    горната клетка на стека.

    Помислете за машинната инструкция CALL X, която се интерпретира като „извикване на подпрограмата, намираща се на адрес X.“ Ако регистърът Rn се използва за съхраняване на адреса за връщане, тогава изпълнението на инструкцията CALL X трябва да продължи както следва (PC е програмният брояч на ядрото на процесора):

    В този запис D е дължината на текущата команда. Адресът за връщане след това завършва в регистъра Rn, откъдето извиканата подпрограма може да го извлече и да го съхрани някъде в RAM за по-късно връщане към извикващата програма.

    Ако се вземе решение за съхраняване на адреса за връщане в началния адрес на извиканата подпрограма, тогава при изпълнение на инструкцията CALL X ядрото на процесора ще трябва да извърши следните операции:

    Това е доста удобно, тъй като адресът за връщане винаги се съхранява на място, точно известно на подпрограмата (по-точно на нейния разработчик).

    И двата описани подхода са работещи и се използват в практиката. Единственият им, но доста съществен недостатък е невъзможността за изпълнение reentrantподпрограми Естеството на повторното влизане на подпрограмата означава, че тя може да бъде извикана отново, преди текущото извикване да завърши. Например, това се случва, ако се извика друга подпрограма вътре в подпрограма, която от своя страна извиква първата. Подпрограмите, които прилагат рекурсивни алгоритми, също трябва да бъдат повторно влизащи.

    По-общ и по-надежден подход е да се използва обратният адрес на стека, за да се съхранява. Когато ядрото на процесора изпълнява инструкция за извикване на подпрограма, адресът за връщане се поставя в горното местоположение на стека и когато изпълнява инструкция за връщане, то изважда този адрес от местоположението на горния стек.

    Купчини

    Стеке списък от елементи от данни, обикновено думи или байтове, чийто достъп е ограничен от следното правило: елементи от този списък могат да се добавят само в края и да се премахват само от края. Краят на списъка се нарича горна част на стека, а началото му се нарича дъно. Тази структура понякога се нарича магазин. Представете си купчина тави в трапезария. Клиентите вземат подноси отгоре, а работниците в кафенето добавят чисти подноси и ги поставят върху стека. Този механизъм за съхранение е добре описан от запомнящата се фраза „последен влязъл, първи излязъл“ (LIFO), което означава, че последният елемент, избутан в стека, е първият, който се премахва от него. Операцията по поставяне на нов елемент в стека често се нарича избутване, а операцията по изваждане на последния елемент от стека се нарича изваждане.

    Данните, съхранявани в основната памет на компютъра, могат да бъдат организирани в стек, така че последователните елементи да се поставят един след друг. Да приемем, че първият елемент се съхранява на адрес BOTTOM и когато нови елементи се избутат в стека, те се подреждат в низходящ ред на последователни адреси. Така стекът расте в посока на намаляване на адресите, което е много честа практика.

    На фиг. Фигура 17.2 показва как в паметта на компютъра е разположен стек, чиито елементи заемат по една дума.

    В долната част съдържа числената стойност 43, а в горната част съдържа 28. За проследяване на адреса на върха на стека се използва регистър на ядрото на процесора, наречен указател на стека (SP). Това може да бъде един от регистрите с общо предназначение или регистър, специално създаден за тази цел.


    Ориз. 17.2. Купчина думи в RAM

    Ако приемем, че RAM паметта е байтово адресируема и думата е с дължина 32 бита, операцията за избутване на стека може да се реализира, както следва:

    Преместете NEWITEM.(SP),

    където инструкцията Subtract изважда изходния операнд 4 от резултатния операнд, съдържащ се в регистъра SP, и поставя резултата в регистъра SP. Тези две команди избутват думата, съхранена на адрес NEWiTEM, в горната част на стека, като първо намаляват указателя на стека с 4. Операцията за изскачане от стека може да се реализира по следния начин:

    Тези две команди преместват стойността, съхранена в горната част на стека, на друго място в RAM на адрес ITEM и след това намаляват указателя на стека с 4, за да сочи към елемента, който сега е в горната част на стека. Резултатът от извършването на всяка от тези операции върху стека, показан на фиг. 17.2, показано на фиг. 17.3.



    Ориз. 17.3. Резултат от стекови операции

    Ако архитектурата поддържа режими на автоматично инкрементално и автоматично декрементално адресиране, командата

    Преместване на NEWITEM,-(SP),

    и изваждането на елемент от стека може да се извърши с помощта на командата

    Преместване (SP)+, ITEM.

    Когато даден стек се използва от програма, обикновено му се разпределя фиксиран брой RAM места. В този случай трябва да се уверите, че програмата не се опитва да избута нови елементи в стека, който е достигнал максималния си размер. Освен това не трябва да се опитва да извади елемент от празен стек, което би се случило в случай на логическа грешка.

    Да приемем, че стекът е запълнен от адрес 2000 (ДОЛУ) до 1500 и след това. Първоначално регистърът, който действа като указател на стека, се зарежда със стойността 2004. Спомнете си, че преди да поставите нов елемент от данни в стека, всеки път от стойността на SP се изважда 4. Следователно първоначалната стойност на 2004 означава, че първият елемент от стека ще има адрес 2000. За да предотвратите опити за избутване на елемент в пълен стек или премахване на елемент от празен стек, трябва леко да усложните изпълнението на операциите за избутване и изскачане на елемент. За извършване на всяка от тези операции горната команда не е достатъчна - тя трябва да бъде заменена с последователността от команди, дадена в таблица 17.1.

    Командата Compare src,dst сравнение изпълнява операцията и задава флаговете за условие според резултата. Не променя стойността на нито един от операндите. Фрагмент a от таблица 17.1 демонстрира изваждане на елемент от стека, а фрагмент b от същата таблица демонстрира поставяне на елемент в стека, докато наблюдава празния и пълен стек при извършване на операции за избутване и изваждане на елемент.

    Таблица 17.1

    Етикет Екип Операнди
    моб_инфо