§ 9.2. Процедуры и функции

Школьный курс python
Содержание

Для чего нужны подпрограммы?

В различных примерах программ мы обращали ваше внимание на повторяющийся код, который значительно увеличивал объем этих программ. Для оптимизации программного кода, чтобы сделать его более понятным и удобным для восприятия, используются подпрограммы. Подпрограмма (англ. subroutine) позволяет вынести часть реализации (некоторую вполне самостоятельную подзадачу или алгоритм) за пределы основной программы и обращаться к ней, по мере необходимости, по имени, из любой части программы. Таким образом, можно не только сократить программный код, но и сделать его более структурированным.
Существует две разновидности подпрограмм: процедуры и функции. Но это деление чисто условное. В C++ между функциями и процедурами нет больших синтаксических различий, поэтому они имеют общее наименование – функции. Функция может принимать аргументы (не обязательно) и возвращать некоторое значение, возможно пустое (void). Если функция не возвращает никакого значения, то такая функция называется процедурой. В отличие от процедур, функции всегда возвращают результат в точку вызова.
Для того, чтобы использовать функцию в программе её нужно объявить, определить и вызвать по имени.

Объявление функции. Прототип

Объявление (англ. – declaration) функции должно следовать непосредственно за директивами #include. Такое объявление функции называется прототипом. Прототип необязателен, если в этом месте программы функция не объявляется, а определяется (англ. – definition), т. е. полностью описывается её алгоритм. Но в сложных программах, в которых используется множество функций, такой подход может усложнить восприятие программного кода. Поэтому общепринятой практикой является прототипирование в начале файла исходного кода (до функции main), а её описание – в конце.
Прототип состоит из указания типа возвращаемого значения, имени функции и () в которых указываются (через запятую) типы параметров. В случае, если функции не передаются аргументы, список параметров оставляется пустым или содержит ключевое слово void. Заканчивается строка объявления – ";".
Задача 1. Рассмотрим использование функции на примере программы, которая должна вывести на экран несколько линий из символов '#'. Длина линий и их количество определяется в процессе работы программы.
Функция line() не возвращает значение, но имеет два целочисленных параметра: количество линий и их символьная длина. Тогда прототип этой функции записывается так:

void line(int, int);

Определение функции

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

Помимо параметров, в функции могут быть и другие локальные переменные.

Если функция не имеет параметров (т. е. данные в функцию не принимаются из-вне), то круглые скобки остаются пустыми.
Тело функции содержит реализацию алгоритма подпрограммы, которая подчиняется общим синтаксическим правилам языка. Например, можно определять переменные фундаментальных и абстрактных типов, осуществлять вызовы библиотечных функций, обращаться к глобальным объектам, которые определены или объявлены вне данной функции, создавать локальные функции и использовать при составлении алгоритма любые операции и инструкции языка. Локальные объекты, которые определены в блоке функции, вне её – не видны.
Составим определение функции line(). Параметры этой функции – x и y, целочисленные переменные, с помощью которых устанавливаются длина линий и их количество. Чтобы в программе не было путаницы с именами, принято (но не запрещено!) чтобы имена параметров и других переменных функции не совпадали с именами переменных в основной программе или именами глобальных объектов.
Определение нашей функции может быть составлено следующим образом:

void line(int x, int y) {
    for (int i = 0; i < x; i++) {
        for (int j = 0; j < y; j++)
            cout << '#';
        cout << '\n';
    }
    cout << endl;
}

Вызов функции

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

Программа 9.2.1
#include <iostream>
using namespace std;

void line(int, int); // Прототип

int main() {
    int n, m;
    cout << "Сколько напечатать линий? => ";
    cin >> n;
    cout << "Какова их длина? => ";
    cin >> m;
    line(n, m); // Вызов
    return 0;
}

void line(int x, int y) { // Определение
    for (int i = 0; i < x; i++) {
        for (int j = 0; j < y; j++)
            cout << '#';
        cout << '\n';
    }
    cout << endl;
}

Вывод

Сколько напечатать линий? => 5
Какова их длина? => 20
####################
####################
####################
####################
####################

Процедуры

Тип функции описанной в программе 9.2.1 – это процедура. Процедура производит определенные действия в программе, но не возвращает никакого значения. Вызов процедуры можно включать в программу отдельной инструкцией в основной программе, в ветвях условной инструкции, в теле цикла, а также в других функциях.
В программе 9.1.11, которую мы написали на предыдущем уроке, содержался многократно повторяющийся код рисования треугольника:

t.fillcolor("Имя_цвета")        
t.begin_fill()
    for i in range(3):            
        t.fd(k)            
        t.rt(120)        
t.end_fill()

Создадим на основе данного кода процедуру рисования равностороннего треугольника и заливку его определенным цветом. Тогда программу 9.1.11 можно переписать следующим образом:

Программа 9.2.2
import turtle as t
from math import sqrt
##################### Параметры холста ######################
w = t.Screen()
w.reset()
w.title('Плитка')
w.bgcolor('black')
w.setup(width = 600, height = 600, startx = -10, starty = 10)
#################### Параметры черепахи #####################
t.speed(10)
t.hideturtle()
t.goto(-300,300)
###################### Решение задачи #######################
def tri(k, color):
    t.fillcolor(color)        
    t.begin_fill()
    for i in range(3):            
        t.fd(k)            
        t.rt(120)        
    t.end_fill()
    
d = int(input("Длина стороны -> "))
n = 600 // d # Определяем количество плиток
m = 600 // int(d * sqrt(3)) # количество рядов
for i in range(m + 1):    
    for j in range(n + 1):  # за один шаг рисуем 2 ряда
        tri(d, "DeepSkyBlue4")    
        t.rt(60)
        tri(d, "dark turquoise")
        t.fd(d)
        tri(d, "dark turquoise")
        t.rt(60)
        tri(d, "DeepSkyBlue4")
        t.lt(180)
        t.fd(d)
        t.rt(60)
    t.bk((n + 1) * d) # Перемещаемся к следующему ряду  
    t.rt(120)    
    t.fd(d)    
    t.lt(60)
    t.fd(d)
    t.lt(60)
w.exitonclick()
t.mainloop()

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

Инструкция return

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

a = myFun();

или возвращение значения функции при вызове другой функции (т. е. использование функции в качестве аргумента):

sqrt(myFun());
cout << myFun();

Чтобы функция смогла возвращать какое-либо значение, полученное в процессе её работы, используется инструкция return. Эта инструкция завершает работу функции и возвращает её значение выражением, которое следует за ключевым словом return. В теле функции должна быть только одна такая инструкция. Допускается использование нескольких инструкций return, но только в том случае, если они находятся в ветвях условной инструкции.
Помимо наличия инструкции return, функцию от процедуры отличает ещё и то, что перед именем в прототипе и в заголовке функции вместо void указан спецификатор типа возвращаемого значения. Тип возвращаемого значения может быть любым, в том числе абстрактным.
Задача 2. Составить программу вычисляющую среднее арифметическое и среднее геометрическое двух введенных чисел a и b. Вычисление среднего арифметического и среднего геометрического оформить в виде функций.

Программа 9.2.3
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
 
double amean(double, double);
double gmean(double, double);
 
int main() {
    double a, b;
    cout << "a = "; cin >> a;
    cout << "b = "; cin >> b;   
    cout << setprecision(3)
         << "Среднее арифметическое "
         << a << " и " << b << " = "
         << amean(a, b)
         << "\nСреднее геометрическое "
         << a << " и " << b << " = "
         << gmean(a, b) 
         << endl;
    return 0;
}
 
double amean(double x, double y) {
    return (x + y) / 2;
}
     
double gmean(double x, double y) {
    return sqrt(x * y);
}

Вывод

a = 2
b = 7
Среднее арифметическое 2 и 7 = 4.5
Среднее геометрическое 2 и 7 = 3.74

Рассмотрим пример, когда функция возвращает логическое значение (логическая функция). Используем ранее составленную программу (8.9.9) для решения следующей задачи с помощью функции (а за одно и ускорим её работу).
Задача 3. Составить программу определяющую является ли число простым. Тест на простоту оформить в виде функции.

Программа 9.2.4
#include <iostream>
#include <cmath>
using namespace std;

bool prime(unsigned);
  
int main() {
    unsigned n;
    cout << "n = "; cin >> n;
    if (prime(n))
        cout << "Это простое число";
    else
		cout << "Число не является простым";
	cout << endl;
    return 0;
}

bool prime(unsigned x) {
	unsigned i = 2;
	bool f = true;
    while (i < sqrt(x)) {
        if (x % i == 0) {
            f = false;
            break;
        }
        else
            ++i;
    }
	return f;
}

Вывод

n = 100
Число не является простым
---------------------
n = 101
Это число простое

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

Программа 9.2.5
#include <iostream>
using namespace std;

int threefirst(int);
int threelast(int);
bool res(int);

int main() {
	int i = 0;
	for (int j = 100000; j < 1000000; j++)
		if (res(j)) ++i;
	cout << i << endl;
	return 0;
}

int threefirst(int x) {
	return x / 1000 % 10 + \
		   x / 10000 % 10 + \
		   x / 100000;
}

int threelast(int x) {
	return x % 10 + \
		   x / 10 % 10 + \
		   x / 100 % 10;
}

bool res(int x) {
	return threefirst(x) == threelast(x);
}

Вывод

50412

Локальные и глобальные переменные. Передача аргументов

Ранее мы упоминали, что блок функции определяет область видимости данных. Что это значит? Приведем пример:

Программа 9.2.6
#include <iostream>
using namespace std;

void fun(int a) { // Параметр — локальная переменная
	a = 10 * a;   // Изменение локальной переменной
	cout << "local a = " 
		 << a 
		 << endl;
}
  
int main() {
    int a = 2;    // Глобальная переменная
	fun(a);       // аргумент - глобальная переменная
	cout << "global a = " 
		 << a     // значение переменной а не изменилось
		 << endl;
    return 0;
}

Вывод

local  a = 20
global a = 2

Параметр a функции fun является локальной переменной - её видимость определяется блоком функции. Поэтому вызов функции fun изменяет значение локальной перемененной a, но не изменяет глобальный объект a. Если имена глобального и локального объекта совпадают, то видимость в функции глобального объекта "отключается".
Параметры функций (во всех программах выше) инициализируются также, как и обычные переменные, при этом значение аргумента копируется. Такой способ передачи аргументов называется передачей аргумента по значению. При таком способе передачи аргументов в функцию невозможно изменить значения объектов в основной программе (глобальных объектов).
Задача 5. Рассмотрим пример функции Swap(), которая обменивает значения двух объектов программы.

Программа 9.2.7
#include <iostream>
using namespace std;

void Swap(int, int);

int main() {
	int a, b;
	cout << "a = "; cin >> a;
	cout << "b = "; cin >> b;
	Swap(a, b);
	cout << "Значения переменных после обмена:\n"
		 << "a = " << a << '\n'
		 << "b = " << b << endl;
	return 0;
}

void Swap(int x, int y) {
	if (x == y)
		return;
	int buf = x;
	x = y;
	y = buf;
}

После запуска программы мы увидим, что глобальные переменные не изменили своего значения. Для того, чтобы появилась возможность изменять значения объектов в основной программе, необходимо связать параметры функции с глобальными объектами. Для этих целей применяются ссылки. Такой способ передачи аргументов называется передача аргументов по ссылке. Для передачи по ссылке используется операция &. Внесем изменения в прототип:

void Swap(int &, int &);

и в заголовок функции:

void Swap(int &x, int &y)

Сохраним и запустим программу на выполнение ещё раз. Переменные поменяют своё значение.
Вывод

a = 2
b = 5
Значения переменных после обмена:
a = 5
b = 2

Обратите внимание на использование инструкции return в процедуре. Здесь (программа 9.2.7 стр. 19) эта инструкция применялась для завершения и выхода из функции (в случае равенства двух значений), а не для возвращения какого-либо значения.

Приложение

Примеры решения задач
Вопросы
Темы сообщений
Задания А
Задания Б
Задания С
Ссылки
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 5,00 из 5)
Загрузка...

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.


Обсуждение закрыто.