Примеры решения задач. Правильное проектирование функций

Правильное проектирование функций

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

Рассмотрим некоторую программу, которая предназначена для работы с массивами; в такой программе могут присутствовать инициализация массива случайными элементами, вывод массива на экран, сортировка массива и еще один вывод:

for (int i=0; i<size; i++) {

numbers[i] = rand()%100;

}

for (int i=0; i<size; i++) {

cout << numbers[i];

}

for (int i=0; i<size-1; i++) {

for (int j=size-1; j>0; j--) {

if (numbers[j] < numbers[j-1]) {

int temp = numbers[j];

numbers[j] = numbers[j-1];

numbers[j-1] = temp;

}

}

}

for (int i=0; i<size; i++) {

cout << numbers[i];

}

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

void initNumbers(int numbers[], int size);

void printNumbers(int numbers[], int size);

void sortNumbers(int numbers[], int size);

int main() {

const int mySize = 10;

int myNumbers[mySize];

initNumbers(myNumbers, mySize);

printNumbers(myNumbers, mySize);

sortNumbers(myNumbers, mySize);

printNumbers(myNumbers, mySize);

}

void initNumbers(int numbers[], int size) {

for (int i=0; i<size; i++) {

numbers[i] = rand()%100;

}

}

void printNumbers(int numbers[], int size) {

for (int i=0; i<size; i++) {

cout << numbers[i];

}

}

void sortNumbers(int numbers[], int size) {

for (int i=0; i<size-1; i++) {

for (int j=size-1; j>0; j--) {

if (numbers[j] < numbers[j-1]) {

int temp = numbers[j];

numbers[j] = numbers[j-1];

numbers[j-1] = temp;

}

}

}

}

Посмотрите на функцию main() и попробуйте теперь понять, что она делает. Все очевидно: функция initNumbers() задает массив, printNumbers() выводит массив на экран, sortNumbers() – сортирует, и, наконец, еще один вызов printNumbers() выводит на экран отсортированный массив. При необходимости эти функции можно поместить в заголовочный файл, что, тем не менее, никак не затронет функцию main().

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

Задача 1. Создайте процедуру, которая принимает в качестве аргумента оценку и выводит её словесный эквивалент.

void printMark(int mark) {

switch (mark) {

case 5: cout << "Отлично\n"; break;

case 4: cout << "Хорошо\n"; break;

case 3: cout << "Удовл.\n"; break;

case 2: cout << "Неуд.\n"; break;

case 1: cout << "Кол\n"; break;

default: cout << "Нет такой оценки\n";

}

}

int main() {

printMark(4);

printMark(1);

printMark(13);

return 0;

}

Задача 2. Создайте функцию, которая принимает в качестве аргументов длины двух катетов прямоугольного треугольника и возвращает длину гипотенузы.

float sqr(float x) {

return x*x;

}

float hyp(float catet1, float catet2) {

return sqrt(sqr(catet1) + sqr(catet2));

}

int main() {

setlocale(LC_ALL, "Russian");

cout << hyp(3,4) << endl;

cout << hyp(3.5,4.2) << endl;

cout << hyp(1.4,10) << endl;

return 0;

}

Задача 3. Два натуральных числа называются взаимно простыми, если единственным общим делителем у них является 1. Напишите функцию, которая принимает два целочисленных аргумента и возвращает true, если они – взаимно простые, и false – иначе.

bool isCoprime(int m, int n) {

int min = m<n? m: n;

for (int i=2; i<=min; i++) {

if (m%i==0 && n%i==0) {

return false;

}

}

return true;

}

int main() {

for (int i=1; i<=20; i++) {

if (isCoprime(i,20)) {

cout << i << endl;

}

}

return 0;

}

Задача 4. Создайте рекурсивную и нерекурсивную функцию, которая принимает положительный целочисленный аргумент и возвращает факториал этого числа. Факториал числа N обозначается через N! и равен произведению чисел от 1 до N. По определению также полагается 0!=1.

int factorial(int n) {

return n==0? 1: n*factorial(n-1);

}

int nrec_factorial(int n) {

int result = 1;

for (int i=1; i<=n; i++) {

result *= i;

}

return result;

}

int main() {

cout << factorial(0) << endl;

cout << nrec_factorial(0) << endl;

cout << factorial(3) << endl;

cout << nrec_factorial(3) << endl;

return 0;

}

Задача 5. Создайте функцию, которая принимает в качестве аргумента целое число и возвращает количество цифр в нём.

int n_digits(int n) {

int result = 0;

int t=n;

while (t>0) {

t /= 10;

result++;

}

return result;

}

int main() {

cout << n_digits(2) << endl;

cout << n_digits(23) << endl;

cout << n_digits(999) << endl;

cout << n_digits(1000) << endl;

return 0;

}

КОНТРОЛЬНЫЕ ВОПРОСЫ

  1. Зачем нужны функции?
  2. Что такое аргументы функции?
  3. Что такое формальные и фактические аргументы?
  4. Чем отличаются глобальные переменные от локальных?
  5. Объясните работу оператора return.
  6. Чем концептуально отличаются функции от процедур?
  7. Что такое рекурсивная функция? Приведите примеры.
  8. Что такое прототип функции? Приведите примеры.
  9. Что такое заголовочный файл и как с ним работать?
  10. Как передавать одномерные и двумерные массивы в качестве аргументов функций? Приведите пример.
  11. Правило хорошего стиля при разработке функций заключается в том, что функция должна решать равно одну задачу, и название функции должно отражать суть этой задачи. Приведите примеры функций, которые согласуются и не согласуются с этим правилом.

Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: