Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Передача в функцию параметров стандартных типов
Написать программу вывода таблицы знамений функции 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. Популярное:
|
Последнее изменение этой страницы: 2016-08-31; Просмотров: 484; Нарушение авторского права страницы