Краткое содержание
Этот урок открывает пятый раздел курса, посвящённый функциям. Он объясняет, для чего нужен раздел: научить разбивать задачу на части с именами, каждую с интерфейсом и контрактом, и решать некоторые задачи, сводя их к меньшему экземпляру их самих, то есть рекурсией; представляет четыре его урока и нить, которая их связывает; напоминает, что раздел считает известным, прежде всего циклы и их инварианты из раздела 4; формулирует задачу, которую решает раздел, задачу программы, которая повторяет одно и то же скопированное вычисление и из-за небрежности при копировании отвечает неверно, а компилятор ничего не сообщает; заранее вводит контракт (предусловие и постусловие) как форму, в которой раздел будет специфицировать каждую часть; и показывает, куда он ведёт, в следующих разделах и в курсе «Программирование на C++ II».
Цели обучения
- Описывать последовательность содержания раздела и объяснять, что каждый урок даёт следующему.
- Вспомнить алгоритмы раздела 4 (трассировку цикла, его инвариант и его вариант) и при необходимости повторить их.
- Заранее понять на примере идею контракта: что часть программы требует от того, кто её использует, и что она гарантирует ему взамен.
- Понимать, что ожидается уметь по окончании раздела, и отличать это от того, что будет изучено в следующих разделах.
Входная проверка
Прежде чем проходить раздел, полезно определить исходный уровень. Эта проверка является самопроверкой: она не оценивается, и её единственная цель состоит в том, чтобы направить занятия. Первые пять вопросов касаются того, что раздел считает известным: трассировки цикла, его инварианта, предусловия спецификации, копирования значения при инициализации и поиска внутри программы из раздела 4 вычисления с собственными входными и выходными данными. Следующие пять в порядке уроков касаются центральных идей раздела: стека вызовов, передачи аргумента, отделения вычисления от представления, рекурсивного определения факториала и выполнения функции, которая вызывает саму себя. Никто не обязан пока отвечать на них верно, а в некоторых из них «пока не знаю» является честным ответом: каждый ответ, верный или нет, объясняет суть дела и называет урок, где она разбирается. Те же умения измеряются снова в конце, в уроке итогов.
Перед началом раздела оцени свою отправную точку. Это короткая самопроверка: она не оценивается, а результат хранится только в твоём браузере.
Вопрос 1Из предыдущих разделовЧто выводит
int n{305}; int c{1}; while (n >= 10) { n = n / 10; ++c; } std::println("{}", c);?Считает итерации и забывает, чтоcначинается с 1.Делает лишнюю итерацию: при условиеn >= 10уже ложно.Путает счётчик со значениемnпосле первой итерации.Вопрос 2Из предыдущих разделовВ цикле
int total{0}; int k{1}; while (k <= n) { total += k; ++k; }, где , какое соотношение выполняется всякий раз, когда вычисляется условие?Выполняется в середине итерации, после сложения и до увеличения, но не тогда, когда вычисляется условие.Выполняется только по окончании: это постусловие, а не инвариант.Это условие цикла, и оно ложно как раз при выходе; инвариант выполняется и тогда.Оно выполняется в начале, когда сумма пуста и ; а если оно выполняется перед итерацией, то выполняется и после неё, потому что итерация прибавляет , а затем увеличивает его. При выходе , и инвариант даёт сумму от 1 до . Урок Рекурсия обосновывает свои функции тем же рассуждением по индукции.Повторить: Проектирование правильных циклов
Вопрос 3Из предыдущих разделовСпецификация деления гласит: «даны целые и ; найти и , такие что и ». Каково её предусловие?
Путает предусловие с постусловием: этому удовлетворяет результат, а от данных этого не требуется.Это условие на выход, а не на вход.При не существует никакого с : задача не имеет решения, и предусловие это исключает.Предусловие есть то, что требуется от данных, чтобы задача имела решение; постусловие есть то, чему удовлетворяет результат. Раздел превращает эту пару в контракт каждой функции: предусловие обязывает вызывающего, а постусловие обязывает функцию.Повторить: От задачи к алгоритму
Вопрос 4Из предыдущих разделовПосле
int a{3}; int b{a}; b = 7;чему равноa?Считает, чтоbиaостаются связанными навсегда; инициализация копирует значение, а не ячейку.Путает копию с неинициализированной переменной:aполучила 3, и ничто этого не изменило.Инициализацияint b{a}копирует в ячейкуbзначение, которое было уa; после этого две ячейки независимы. Урок Параметры и область видимости показывает, что параметр инициализируется так же, копией аргумента, и учит другой возможности: второму имени для той же ячейки.Повторить: Переменные и память
Вопрос 5Из предыдущих разделовВ программе урока 4.4, которая выводит простые числа, меньшие 100, есть цикл, перебирающий кандидатов, а внутри него другой, проверяющий их делители. Какая часть является вычислением с собственными входными и выходными данными, которое стоило бы назвать?
Путает вход и выход всей программы с входом и выходом внутреннего вычисления.Это инструкция, а не вычисление с собственным смыслом: у неё нет входа и результата, которые кто-то захотел бы использовать.Путает данные с задачей: переменная хранит результат вычисления, но не является вычислением.Внутренний цикл не зависит ни от чего, кроме кандидата, и его результат есть «да» или «нет». Если дать ему имя, внешний цикл читается как его спецификация: «для каждого кандидата, если он простой, вывести его». Урок Проектирование с помощью функций именно так переписывает эту программу.Повторить: Численные алгоритмы
Вопрос 6Программа начинается в
main;mainвызывает функциюg, аgвызывает другую функциюh. Какие вызовы не завершены, пока выполняетсяh?Считает, что вызывающий исчезает, сделав вызов; на самом деле он ждёт, чтобы продолжить с того места, где остановился.Считает, чтоgзавершилась, вызвавh; она завершится, когдаhвернёт ей управление.Это честный ответ: урок 5.1 объясняет это с помощью рисунка стека.Вопрос 7Дана функция
int f(int x) { x = x + 1; return x; }. Что выводитint a{3}; int b{f(a)}; std::println("{} {}", a, b);?Считает, что функция изменяет аргумент; по умолчанию она получает копию.Считает, что изменениеxне затрагивает даже возвращаемое значение.Считает, что параметр нельзя изменять; можно, если только он не объявленconst.Параметрxесть переменная функцииf, которая инициализируется копией аргумента: функция изменяет копию и возвращает 4, аaсохраняет свои 3. Это передача по значению, способ, принятый в C++ по умолчанию.Повторить: Параметры и область видимости
Вопрос 8Программа читает дату и должна вывести её день недели. Какая декомпозиция отделяет вычисление от представления?
Смешивает две задачи: вычисленное нельзя ни использовать повторно, ни сравнить с ожидаемым значением.Разделяет задачи, но связывает их скрытым эффектом: тот, кто читает вызов, не видит, откуда берутся данные.Это честный ответ: урок 5.3 учит решать, какие функции писать.Так вычисление тестируется на числах, не глядя на экран, и может служить другой программе, которая выводит результат иначе. Урок 5.3 называет этот критерий «одна функция, одна задача».Повторить: Проектирование с помощью функций
Вопрос 9Какое из следующих определений задаёт для любого натурального ?
Без базового случая определение никогда не останавливается: отсылало бы к , и так без конца.Правильно определяет при , но оставляет неопределённым , которое равно 1.Определяет через само себя: определение циклично и не сводит задачу к меньшей.Вопрос 10Дана функция
int f(int n) { if (n == 0) { return 0; } return n + f(n - 1); }. Что выводитstd::println("{}", f(3));?Считает, что выполняется только один вызов, или путает аргумент с результатом.Считает, чтоreturn 0базового случая есть результат всей цепочки; это лишь результат последнего вызова.Имя функции известно начиная с её заголовка, и его можно использовать в её собственном теле.f(3)ждётf(2), которая ждётf(1), которая ждётf(0); последняя возвращает 0, и на обратном пути прибавляются 1, 2 и 3. Функция вычисляет , ту же сумму, что и цикл из второго вопроса.Повторить: Рекурсия
Загрузка содержимого…