- •Экзамен 374 Предварительные рассуждения Вступительное слово
- •Исторические факты
- •Начнем!
- •Проба пера
- •Открытие сохраненного проекта
- •Вывод данных
- •Типы данных
- •Хороший стиль программирования
- •Переменные и константы
- •Практический пример
- •Ввод данных
- •Например:
- •Пример:
- •Арифметические операции с числами
- •Литералы
- •Некоторые примеры
- •Домашнее задание
- •Напишите программу, которая вводит число из трех цифр, разделяет число на отдельные цифры и печатает их отдельно друг от друга с тремя пробелами между ними. Преобразование типов
- •Перечисляемые типы
- •Типичная ошибка
- •Хороший стиль программирования
- •Типичная ошибка
- •Выражения
- •Оператор if
- •Структура программы
- •Логические операции
- •Структура множественного выбора switch
- •Практический пример
- •Цикл for
- •Практический пример
- •Цикл do-while
- •Домашнее задание
- •Вызов функции
- •Прототипы функций
- •Разбор программы
- •Область видимости
- •Аргументы по умолчанию
- •Встраивание
- •Перегрузка функций
- •Учебный пример перегруженных функций. Иллюстрация перегрузки
- •Результат работы программы
- •Практические примеры
- •Домашнее задание
- •Примеры домашней работы урока 1 Пример №1
- •Как работает программа
- •Пример №2
- •Как работает программа
- •Примеры домашних работ на создание функций Пример №1
- •Как работает программа
- •Пример №2
- •Как работает программа
- •Массивы
- •Объявление массивов
- •Примеры использования массивов
- •Программа 1
- •Программа 2
- •Обратите внимание!
- •Типичная ошибка программирования
- •Типичная ошибка программирования
- •Программа 3
- •Типичная ошибка программирования
- •Замечание по технике программирования
- •Программа 4
- •Программа 5
- •Программа нахождения минимального и максимального элементов массива
- •Сортировка массивов
- •Домашнее задание
- •Что такое указатели?
- •За кулисами...
- •Как работать с указателями?..
- •Зачем нужны указатели?
- •Указатели и Массивы.
- •Примеры задач
- •Пример 1
- •Пример 2
- •Пример 3
- •Указатели - аргументы функций.
- •Ссылочные параметры
- •Примеры решения задач
- •Домашнее задание
- •Операторы свободной памяти new и delete
- •Функции работы со строками из библиотеки обработки строк
- •Пример 1.
- •Пример2
- •Пример 3
- •Пример задачи на новый материал
- •Домашнее задание
- •Двухмерные массивы, как частный случай многомерных массивов
- •Программа.
- •Результаты работы программы.
- •Многомерные динамические массивы
- •Пример на многомерные динамические массивы
- •Домашнее задание
- •Рекурсия
- •Рекурсии или итерации
- •Указатели на функции
- •Пример №1
- •Результат выполнения программы:
- •Пример №2
- •Результат выполнения программы
- •Пример №3
- •Результаты выполнения программы
- •Определения структур
- •Пример #1 на использование структур
- •Пример #2 на использование структур
- •Оператор указателя на структуру
- •Домашнее задание
- •Тест по c Группа ___________________ф. И. О. ______________________
- •Объектно-ориентированное программирование.
- •Наследование (Inheritance).
- •Инкапсуляция (Encapsulation).
- •Определение класса
- •Конструкторы и деструкторы Инициализация объектов класса: конструкторы
- •Основное назначение конструкторов - инициализация объектов.
- •Использование конструкторов с аргументами по умолчанию
- •Если параметры не передаются конструктору, в определении объекта не нужно включать пустые круглые скобки.
- •Использование деструкторов
- •Когда вызываются конструкторы и деструкторы.
- •Домашнее задание
- •Конструктор копирования
- •Синтаксис конструктора копирования
- •Памятка
- •Пример использования конструктора копирования.
- •Перегруженные конструкторы
- •Экскурс в историю
- •Послесловие к примеру
- •Маленькое замечание
- •Домашнее задание
- •Создание класса ''строка''
- •Перегрузка операций.
- •Общие принципы перегрузки операторов.
- •Преобразования, определяемые классом
- •Пример строкового класса с перегруженными операторами
- •Домашнее задание
- •Дружественные функции (Friend Functions)
- •Пример строкового класса с перегруженными операторами и дружественными функциями
- •Перегрузка операторов new и delete
- •Перегрузка оператора индексирования
- •Класс вектор. Часть1.
- •Класс вектор. Часть 2.
- •Класс вектор. Часть 3.
- •Домашнее задание
- •Наследование (Inheritance). Часть 1.
- •Наследование (Inheritance). Часть 2.
- •Множественное наследование (multiple inheritance)
- •Пример множественного наследования
- •Домашнее задание
- •Статические члены данных
- •Раннее и позднее связывание
- •Виртуальные функции
- •Пример.
- •Абстрактные классы
- •Виртуальный базовый класс
- •Практический пример
- •Домашнее задание
- •Потоки ввода-вывода.
- •Iostream.H: stream - поток, "I" - сокр. Input - ввод, "o" - сокр. Output - вывод.
- •Предопределенные потоки.
- •Операции помещения в поток и извлечения из потока.
- •Файловый ввод-вывод с применением потоков.
- •Конструкторы файловых потоков.
- •Функции для открытия и закрытия файлов.
- •Функции для обмена с потоками.
- •Часто применяемые функции потока.
- •Ввод/вывод массива в/из файл(-а).
- •Практический пример: перекодировка файла.
- •Домашнее задание
- •Немного о файлах...
- •И снова файлы...
- •Пример "Телефонная книга"
- •Файл abonent.H
- •Форматирование данных при обменах с потоками.
- •Состояние потока.
- •Использование аргументов командной строки.
- •Ввод/вывод в с.
- •Домашнее задание
- •Определение шаблонов функций
- •Переопределение шаблонов функций
- •Шаблоны классов
- •Шаблонный класс вектор
- •Шаблонный класс вектор
- •Шаблонный класс вектор
- •Введение
- •Обработка исключительных ситуаций
- •Практический пример
- •Программа
- •Домашнее задание
- •Экзамен
Домашнее задание
Добавить в пример использования многомерных динамических массивов функцию сортировки строк.
Дана квадратная матрица порядка n (n строк, n столбцов). Найти наибольшее из значений элементов, расположенных в тёмно-синих частях матриц.
Рекурсия
Программы, которые мы обсуждали до сих пор, в общем случае состояли из функций, которые вызывали какие-либо другие функции в строгой иерархической манере. В некоторых случаях полезно иметь функции, которые вызывают сами себя.
Рекурсивная функция - это функция, которая вызывает сама себя либо непосредственно, либо косвенно с помощью другой функции. |
Давайте рассмотрим, что же такое рекурсия.
Рекурсивная задача в общем случае разбивается на ряд этапов. Для решения задачи вызывается рекурсивная функция. Эта функция знает, как решать только простейшую часть задачи - так называемую базовую задачу (или несколько таких задач). Если эта функция вызывается для решения базовой задачи, она просто возвращает результат. Если функция вызывается для решения более сложной задачи, она делит эту задачу на две части: одну часть, которую функция умеет решать, и другую, которую функция решать не умеет. Чтобы сделать рекурсию выполнимой, последняя часть должна быть похожа на исходную задачу, но быть по сравнению с ней несколько проще или несколько меньше. Поскольку эта новая задача подобна исходной, функция вызывает новую копию самой себя, чтобы начать работать над меньшей проблемой - это называется рекурсивным вызовом, или шагом рекурсии. Шаг рекурсии включает ключевое слово return, так как в дальнейшем его результат будет объединен с той частью задачи, которую функция умеет решать, и сформируется конечный результат, который будет передан обратно в исходное место вызова, возможно, в main.
Шаг рекурсии выполняется до тех пор, пока исходное обращение к функции не закрыто, т.е. пока еще не закончено выполнение функции. Шаг рекурсии может приводить к большому числу таких рекурсивных вызовов, поскольку функция продолжает деление каждой новой подзадачи на две части. Чтобы завершить процесс рекурсии, каждый раз, как функция вызывает саму себя с несколько упрощенной версией исходной задачи, должна формироваться последовательность все меньших и меньших задач, в конце концов сходящаяся к базовой задаче. В этот момент функция распознает базовую задачу, возвращает результат предыдущей копии функции и последовательность возвратов повторяет весь путь назад, пока не дойдет до первоначального вызова и не возвратит конечный результат в функцию main.
В качестве примера рассмотрим одну из задач домашнего задания, а именно: вычисление факториала. Напомним, факториал неотрицательного целого числа n, записываемый как n!, равен n*(n-1)*(n-2)*...*1
n! = n*(n-1)*(n-2)*...*1
причем считается, что 1! = 1 и 0! = 1. Например, 5! вычисляется как произведение 5*4*3*2*1 и равен 120.
Факториал целого числа, number, большего или равного 0, может быть вычислен итеративно (нерекурсивно), например, с помощью оператора for следующие образом:
factorial = 1; for (int counter = number; counter >=1; counter--) factorial *= counter; |
Рекурсивное определение функции факториал дается следующим соотношением: n!=n*(n-1)! Например, факториал 5! очевидно равен 5*4!. В самом деле: 5!=5*4*3*2*1 5!=5*(4*3*2*l) 5!=5*4!
Вычисление 5! должно происходить в соответствии с рисунком.
Рекурсивное вычисление 5!
|
Слева представлен процесс рекурсивных вызовов, который происходит до тех пор, пока не будет вычислен 1! = 1 (базовая задача), что приведет к завершению рекурсии. Справа показаны значения, возвращаемые из каждого рекурсивного вызова оператору вызова до тех пор, пока не будет вычислено и возвращено окончательное значение.
Приведем программу вычисления факториалов чисел от 0 до 10 с помощью рекурсивной функции. Рекурсивная функция factorial сначала проверяет, истинно ли условие завершения рекурсии, т.е. меньше или равно 1 значение number. Если действительно number меньше или равно 1, factorial возвращает 1, никаких дальнейших рекурсий не нужно и программа завершает свою работу. Если number больше 1, оператор
return number * factorial (number - 1);
представляет задачу как произведение number и рекурсивного вызова factorial, вычисляющего факториал величины number - 1. Отметим, что factorial(number - 1) является упрощенной задачей по сравнению с исходным вычислением factorial(number).
/*Рекурсивная функция факториала*/
#include<iostream.h> unsigned factorial(int); void main(void) { for (int i = 0; i <= 10; i++) cout << i << "! = " << factorial(i) << '\n'; }
/*рекурсивное описание функции факториала*/ unsigned factorial ( int number) { if (number <= 1) return 1; else return number * factorial(number - 1); } |
Необходимо отметить, что значение факториала растет очень быстро, что не позволяет напечатать много значений факториала до того, как будет превышен допустимый предел типа unsigned.
Типичная ошибка программирования Забывают возвращать значения из рекурсивной функции, когда оно необходимо.
Типичная ошибка программирования Пропуск базовой задачи или неправильная запись шага рекурсии, из-за чего процесс не сходится к базовой задаче, приводят к бесконечной рекурсии и существенным затратам памяти. Это аналог бесконечного цикла в итеративном (нерекурсивном) процессе. Бесконечная рекурсия может быть также вызвана вводом неправильной величины.
В чем состоит ошибка, если функцию факториала, приведенную выше, изменить следующим образом:
unsigned factorial ( int number)
{
return number * factorial(number - 1);
}
Не задана базовая задача, следовательно, приходим к бесконечному циклу вызовов функцией самой себя. |