Написать программу вывода таблицы знамений функции Ch х (гиперболический косинус) для аргумента, изменяющегося в заданных пределах с заданным шагом. Значения функции вычислять с помощью разложения в ряд Тейлора с точностью eps. Алгоритм работы программы также приводился ранее: для каждого из серии значений аргумента вычисляется и затем выводится на экран значение функции. Очевидно, что подсчет суммы ряда для одного значения аргумента логично оформить в виде отдельной функции.
Нашей функции подсчета суммы ряда требуется получить извне значение аргумента и точность. Пусть эти величины, а также результат имеют тип double. Следовательно, заголовок функции может выглядеть так:
double cosh(double х. double eps): Мы видим, что для вычисления суммы ряда понадобятся две промежуточные переменные — для хранения очередного члена ряда и его номера. Эти переменные должны быть описаны внутри функции, поскольку вне ее они не нужны Рассмотрим текст программы:
#include <math.h>
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include <stdio.h> //0
using std::cin;
using std::cout;
using std::endl;
using std::setw;
int main(){
double cosh{double x. double eps);
double Xn, Xk, dX, eps;
printf("Enter Xn. Xk. dX. eps \n");
scanf("%lf%lf%lf%lf%lf",&Xn,&Xk,&dX,&eps);
printf("................................ \n");
printf(" | X | Y |\n");
printf(" ................................\n");
for (double x = Xn; x <= Xk; x += dX)
pr1ntf("|%9.2lf |%14.6g |\n", x, cosh(x, eps);
printf(" ................................\n"):
return 0;
)
double cosh(double x, double eps)
{ const int Maxlter = 500; // максимальное количество итераций
double ch = 1, у = ch; // первый чпен ряда и нач. значение суипы
for (int n = 0; fabs(ch) > eps;n++) {
ch *= х * х /((2 * n • 1)*(2 * n + 2)); // член ряда
у += ch; // добавление члена ряда к сумме
if [n > MaxIter) { puts("Ряд расходится!\n"); return 0;}
}
return у;
}
JЗа счет использования функции программа получилась более ясной и компактной, потому что задача была разделена на две: вычисление функции и печать таблицы. Кроме того, написанную функцию можно при необходимости без изменений перенести в другую программу или поместить в библиотеку.
Если определение функции размешается после ее вызова, то перед функцией, в которой он выполняется, размешают прототип (заголовок). Обычно заголовки всех используемых в программе функций размещают в самом начале файла или в отдельном заголовочном файле. Заголовок нужен для того, чтобы компилятор мог проверить правильность вызова функции. Стандартные заголовочные файлы, которые мы подключаем к программам, содержат прототипы функций библиотеки именно с этой целью.
Обратите внимание на спецификацию формата д. Она применяется для вывода вещественных чисел в широком диапазоне значений. Первое число модификатора (14) задает, как и для других спецификаций, ширину отводимого под число поля, а второе (6) — не точность, как в формате f. а количество значащих цифр. При этом число выводится либо в формате f, либо в формате е (с порядком) в зависимости от того, какой из них получится короче.
Для сигнализации об ошибке нужно сформировать в функции и передать наружу признак успешного завершения подсчета суммы, который должен анализироваться в вызывающей программе. Такой подход часто применяется в стандартных функциях. В качестве признака используется либо возвращаемое значение, которое не входит в множество допустимых (например, отрицательное число при поиске номера элемента в массиве или ноль для указателя), либо отдельный параметр ошибки.
Обычно параметр ошибки представляет собой целую величину, ненулевые значения которой сигнализируют о различных ошибках в функции. Если ошибка может произойти всего одна, параметру можно назначить тип bool. Параметр передается в вызывающую программу и там анализируется.
Сейчас самое время рассмотреть механизм передачи параметров в функцию. Он весьма прост. Когда мы пишем всписке параметров функции выражение вида double х, это значит, что в функцию при ее вызове должно быть передано значение соответствующего аргумента. Для этого в стеке создается его копия, с которой работает функция. Естественно, что изменение этой копии не может оказать никакого влияния на ячейку памяти, в которой хранится сам параметр. Кстати, именно поэтому на месте такого параметра можно при вызове задавать и выражение.
. Передача в функцию имени функции
Назовем функцию вывода таблицы значений print_tabl. Прежде всего надо определить ее интерфейс. Для того чтобы вывести таблицу, нашей функции потребуется знать диапазон и шаг изменения значений аргумента, а также какую, собственно, функцию мы собираемся вычислять. Всё? Нет. не все: забыли, что в функцию вычисления суммы ряда надо передавать точность, поэтому точность следует включить в список параметров вызывающей ее функции prtnt_tabl. Функция print_tabl не возвращает никакого значения, то есть перед ее именем надо указать void.
Как передать в функцию имя функции? Точно так же, как и любую другую величину: в списке параметров перед именем параметра указать его тип. До этого момента мы передавали в функцию величины стандартных типов, а теперь нам потребуется определить собственный тип. Тип функции определяется типом ее возвращаемого значения и типом се параметров. Для нашей функции это выглядит так
double (*fun)(double, double): Здесь описывается указатель по имени fun на функцию, получающую два аргумента типа double и возвращающую значение того же типа (от параметров по умолча-
Часто, если описание типа сложное, с целью улучшения читаемости программы задают для него синоним с помощью ключевого слова typedef:
typedef double (*Pfun)(double, double): В этом операторе задается тип Pfun. который можно использовать наряду со стандартными типами при описании переменных. Таким образом, заголовок функции печати таблицы должен иметь вид:
void print_tabl(Pfun fun, double Xn, double Xk, double dX, double eps);
Запишем теперь текст программы, сведя к минимуму диагностику ошибок (при превышении максимально допустимого количества итераций функция завершается, возвращая 0, а вызывающая программа бесстрастно выводит это значение):
#include <math.h>
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include <stdio.h> //0
using std::cin;
using std::cout;
using std::endl;
using std::setw;
typedef double(*Pfun)(const double, const double);
void print_tabl(Pfun,const double,const double Xn,const double Xk,
const double dX,const double eps);
double cosh{const double x, const double eps);
int main(){
double Xn, Xk, dX, eps;
printf("Enter Xn. Xk. dX. eps \n");
scanf("%lf%lf%lf%lf%lf",&Xn,&Xk,&dX,&eps);
print_tabl(cosh,Xn,Xk,dX,eps);
return 0;
)
void print_tabl(Pfun,const double,const double Xn,const double Xk,
const double dX,const double eps){
printf("Enter Xn. Xk. dX. eps \n");
scanf("%lf%lf%lf%lf%lf",&Xn,&Xk,&dX,&eps);
printf("................................ \n");
printf(" | X | Y |\n");
printf(" ................................\n");
for (double x = Xn; x <= Xk; x += dX)
pr1ntf("|%9.2lf |%14.6g |\n", x, cosh(x, eps);
printf(" ................................\n"):
return 0;
double cosh(double x, double eps)
{ const int Maxlter = 500; // максимальное количество итераций
double ch = 1, у = ch; // первый чпен ряда и нач. значение суипы
for (int п = 0; fabs(ch) > eps;n++) {
ch *= х * х /((2 * n • 1)*(2 * n + 2)); // член ряда
у += ch; // добавление члена ряда к сумме
if [n > MaxIter) { puts("Ряд расходится!\n"); return 0;}
}
return у;
}
Функция print_labl предназначена для вывода таблицы значений любой функции, принимающей два аргумента типа double и возвращающей значение того же типа.
Как видите, наряду с большей общностью мы добились и лучшего структурирования программы, разбив ее на две логически не связанные подзадачи: вычисление функции и вывод таблицы. В главной программе остался только ввод исходных данных и вызов функции.
Передача одномерных массивов в функцию
Даны два массива из п целых чисел каждый. Определить, в каком из них больше положительных элементов.
Очевидно, что для решения этой задачи потребуется подсчитать количество положительных элементов в двух массивах, то есть выполнить для обоих массивов одни и те же действия. Следовательно, эти действия надо поместить в функцию. Интерфейс функции: входные данные — массив и количество его элементов, результат — количество положительных элементов в массиве. Таким образом, заголовок функции должен иметь вид:
int n_posit(const int *a. const int n); Имя массива представляет собой указатель на его нулевой элемент, поэтому в функцию массивы передаются через указатели. Количество элементов в массиве должно передаваться отдельным параметром, потому что. в отличие от строк символов, использующих признак конца строки, для массивов общего вида никакого признака конца массива не существует.
#include <math.h>
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include <stdio.h> //0
using std::cin;
using std::cout;
using std::endl;
using std::setw;
#include <iostream.h>
int n_posit(const int *a. const int n);// прототип функции
int main(){
int i, n;
cout<< "Введите количество элементов: "; cin>> n;
int *a = new int(n]:
int *b= new int[n];
cout <<"Введите элементы первого массива: ";
for (i= 0; i < п;i++) cin>> a[i];
cout << "Введите элементы второго массива: ";
for (i=0;i < n;i++) cin>>b[i];
if (n_posit(a, n) > n_posit(b, n))
cout << " В первой попоимтельных больше" << endl;
else 1f(n_posit(a, n) < n_poslt(b, n))
cout << " Во второй положительных больие" << endl;
else
cout << " Одинановое количество" << endl;
return 0;
}
int n_posit(const int *a. const int n) (
int count = 0;
for (int i=0; i < n; i++) if (a[i] > 0) count++;
return count; }
В этой программе место под массивы выделяется в динамической области памяти, поскольку в задании не указано конкретное количество элементов. Однако функцию n_posit можно без изменений применять и для «обычных» массивов, потому что для каждого из них имя тоже является указателем на нулевой элемент, только константным.
Заслуживает рассмотрения способ анализа результатов работы функции. Как видите, функция вызывается в составе выражения в условном операторе. Для перебора всех трех вариантов результата приходится вызывать ее для каждого массива дважды, что для больших массивов, конечно, нерационально. Чтобы избежать повторного вызова, можно завести две переменные, в которые записываются результаты обработки обоих массивов, а затем использовать эти переменные в условных операторах.
Надо сказать, что современные компиляторы обладают широкими возможностями оптимизации и сами отслеживают подобные ситуации, преобразуя код программы, но это не означает, что на эффективность своих программ вообще не надо обращать внимания. Главным же критерием при выборе варианта написания про- , граммы, тем не менее, остается простота ее структуры и читаемость.
Мы не получили выигрыша в длине приведенной выше программы, использовав для вычисления количества положительных элементов функцию, но причина этому — лишь простота задачи. В реальных программах в виде отдельной функции оформляются более крупные законченные фрагменты. Разные авторы рекомендуют различные конкретные цифры, задающие длину функций, но сходятся в одном: функция должна быть не очень короткой, но и не очень длинной. Чаще всего советуют создавать функции длиной в один-два экрана. Впрочем, придерживаться разумной умеренности бывает полезно не только при написании программ...
Задача 7.3. Передача строк в функцию
Написать программу, определяющую, сколько чисел содержится в каждой строке текстовою файла. Длина каж/кш строки не превышает 100 символов. Эту задачу можно разбить на две: ввод данных из файла и их анализ. Для каждой строки проверка выполняется отдельно, поэтому в виде функции логично оформить поиск и подсчет количества чисел в одной строке. На «сод функции будем подавать строку, а на выходе получать количество чисел в этой строке.
Отличие передачи в функцию строки от передачи обычного массива состоит в том, что можно не передавать отдельным параметром размерность строки, а определять конец строки внутри функции по нуль-символу. Для простоты предположим, что числа могут быть либо целые, либо вещественные с фиксированной точкой и непустой дробной частью. Распространить действие программы на другие виды чисел предоставляется вам в виде самостоятельного упражнения.
#include <fstream.h>
#include <ctype.h>
#include <string.h>
#include <iomanip>
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h> //0
using std::cin;
using std::cout;
using std::endl;
using std::setw;
int main(){
ifstream fin("test.txt", ios::in | ios::nocreate); // 6
if (!fin) { cout <<" No test.txt "<<endl; return 1; }
const int len = 101;
int i=1;
char str[len];
while (fin.getline(str, len)) { // 7
cout << " В строке "<<i<< " содержится"<<num_num(str)<<
"чисел"<<endl;
i++;
return 0;
}
int num_num(const char *str){
int count=0;
while (*str){
if (isdigit(*str)&&!isdigit(*(str+1))&&*(str+1)!='.') count++;
str++;}
return count;
}
Увеличение счетчика чисел в функции nun_num происходит каждый раз. когда заканчивается число, то есть если после цифры стоит не цифра и не точка. Цикл .заканчивается по достижении нуль-символа
Передача двумерных массивов в функцию
Написать программу, определяющую в какой строке целочисленной матрицы m х n находится самая длинная серия одинаковых элементов. Под серией имеются в виду элементы, расположенные подряд. В виде функции здесь удобно оформить решение основной задачи, оставив главной программе только ввод исходных данных и вывод результатов. Для удобства отладки ввод массива в программе выполняется из текстового файла. Первая строка файла содержит значения для m и n, . каждая следующая строка — набор чисел для одной строки матрицы. Память под массив выделяется в цикле для того, чтобы можно было задавать обе размерности массива в виде переменны)
#include <fstream.h>
int ser_equals(int **a, const int m, const int n);
int main(){
ifstream fin ("matrix.txt" , ios::in | 1os::nocreate);
1f (!f1n) { cout << "Нет файла matrix.txt" << endl; return 0; }
int m, n, i, j;
fin >> m >> n;
int **a = new int *[m]; // выделение памяти
for(i = 0; i < m; i++) a[i] = new int [n];
for (i=0; i < m; i++) // ввод массива
for (j = 0; j < n; j++) fin >> a[i][j]
int line = ser_equals(a, m.n); // вызов функции
if (line >= 0) cout<< " Самая длинная серия в строке " <<line;
else cout << " Серий одинаковых элементов нет "; return 0: }
int ser_equals(int **а, const int m, const int n){
int i, j, count, line = -1, maxcount = 0;
for (1 =0; i < m: i++){
count = 0; //1
for (j=0; j < n - 1; j++) {
if (a[i][j] == a[i][j + 1] ) count++ //2
else { //3
if (count > maxcount) {
maxcount = count; line= i: } //4
count = 0;
}
)
if (count > maxcount) {
maxcount = count: line = i: } //5
}
return line;
}
Алгоритм работы функции прост: в каждой строке выполняется сравнение соседних элементов (оператор 2). Если они равны, мы находимся внутри серии, при этом увеличиваем ее текущую длину. Она накапливается в переменной count, которая обнуляется перед обработкой каждой строки (оператор 1). Если же элементы не равны, это означает либо окончание серии, либо просто одиночный элемент (оператор 3). В этом случае надо посмотреть, не является ли данная серия самой длинной из рассмотренных и, если да, то запомнить ее длину и номер строки, в которой она встретилась (оператор 4). Для подготовки к анализу следующих серий в этой же строке надо обнулить счетчик count. Аналогичная проверка после цикла просмотра строки (оператор 5) выполняется для серии, которая расположена в конце строки, поскольку в этом случае ветвь el se выполняться не будет.
Если в массиве нет ни одной серии одинаковых элементов, функция вернет значение, равное-1.