Можно сделать вывод, что для передачи информации между функциями предпочтительнее пользоваться параметрами и возвращаемыми значениями.
Вероятность ошибок при таком подходе возрастает с увеличением списка. Считается, что восемь параметров – это приемлемый максимум. В качестве альтернативы длинному списку можно использовать в качестве параметра класс, массив или контейнер. Он способен содержать группу значений.
Аналогично программа может возвращать только одно значение. Если же логика требует нескольких, некоторые параметры объявляются ссылками, чтобы функция могла непосредственно модифицировать значения соответствующих фактических аргументов и использовать эти параметры для возврата дополнительных значений, либо некоторый класс или контейнер, содержащий группу значений, объявляется типом, возвращаемым функцией.
Каковы две формы инструкции return? Объясните, в каких случаях следует использовать первую, а в каких вторую форму.
Найдите в данной функции потенциальную ошибку времени выполнения:
vectorstring readText( ) {
vectorstring text;
string word;
while ( cin word ) {
text.push_back( word );
// ...
}
// ....
return text;
}
Каким способом вы вернули бы из функции несколько значений? Опишите достоинства и недостатки вашего подхода
7.5. Рекурсия
Функция, которая прямо или косвенно вызывает сама себя, называется рекурсивной. Например:
int rgcd( int vl, int v2 )
{
if ( v2 != 0 )
return rgcd( v2, vl%v2 );
return vl;
}
Такая функция обязательно должна определять условие окончания, в противном случае рекурсия будет продолжаться бесконечно. Подобную ошибку так иногда и называют – бесконечная рекурсия. Для rgcd() условием окончания является равенство нулю остатка.
Вызов
rgcd( 15, 123 );
возвращает 3 (см. табл. 7.1).
Таблица 7.1. Трассировка вызова rgcd (15,123)
|
v1 |
v2 |
return |
|
15 |
123 |
rgcd(123,15) |
|
123 |
15 |
rgcd(15,3) |
|
15 |
3 |
rgcd(3,0) |
|
3 |
0 |
3 |
Последний вызов,
rgcd(3,0);
удовлетворяет условию окончания. Функция возвращает наибольший общий делитель, он же возвращается и каждым предшествующим вызовом. Говорят, что значение всплывает (percolates) вверх, пока управление не вернется в функцию, вызвавшую rgcd() в первый раз.
Рекурсивные функции обычно выполняются медленнее, чем их нерекурсивные (итеративные) аналоги. Это связано с затратами времени на вызов функции. Однако, как правило, они компактнее и понятнее.
Приведем пример. Факториалом числа n является произведение натуральных чисел от 1 до n. Так, факториал 5 равен 120: 1 ? 2 ? 3 ? 4 ? 5 = 120.
Вычислять факториал удобно с помощью рекурсивной функции:
unsigned long
factorial( int val ) {
if ( val 1 )
return val * factorial( val-1 );
return 1;
}
Рекурсия обрывается по достижении val значения 1.
Перепишите factorial() как итеративную функцию.
Что произойдет, если условием окончания factorial() будет следующее:
if ( val != 0 )
7.6. Встроенные функции
Рассмотрим следующую функцию min():
int min( int vl, int v2 )
{
return( vl v2 ? vl : v2 );
}
Преимущества определения функции для такой небольшой операции таковы:
* как правило, проще прочесть и интерпретировать вызов min(), чем читать условный оператор и вникать в смысл его действий, особенно если v1 и v2 являются сложными выражениями;
модифицировать одну локализованную реализацию в приложении легче, чем 300. Например, если будет решено изменить проверку на:
( vl == v2 || vl v2 )
* поиск каждого ее вхождения будет утомительным и с большой долей вероятности приведет к ошибкам;
* семантика единообразна. Все проверки выполняются одинаково;
* функция может быть повторно использована в другом приложении.