Перейти к содержимому
Topos Uranos

Краткое содержание

Этот урок составляет начальный раздел курса и излагает его учебный план целиком. Он называет цель курса, а именно само программирование, а не какой-то язык; объясняет, почему его основы преподаются на C++, языке, который не скрывает машину, а не на языке с более пологой кривой обучения; указывает, каким студентам курс адресован, то немногое, что он считает известным, и курсы, к которым он ведёт; формулирует семь общих учебных целей и разделы, которые вносят вклад в каждую из них; описывает восемь содержательных разделов курса и уроки, из которых они состоят; оценивает общую трудоёмкость курса, её распределение по разделам, рекомендуемый темп занятий и число кредитов; объясняет, как устроены уроки, и, наконец, даёт советы о том, как изучать курс, как пользоваться ассистентами на основе искусственного интеллекта и как вести контроль версий.

Цели обучения

  1. Описывать последовательность восьми разделов курса и объяснять, что каждый из них даёт следующим.
  2. Формулировать семь общих учебных целей курса и узнавать, какие разделы вносят вклад в каждую из них.
  3. Объяснять на примере, почему курс преподаёт основы программирования на C++, а не на языке, который скрывает типы и память.
  4. Рассчитывать по оценке часов для каждого раздела трудоёмкость курса или его части и планировать на её основе собственный темп занятий.

Цель

Курс учит писать правильные программы с нуля: превращать задачу, сформулированную словами, в точную последовательность инструкций, выбирать, как представить данные, управлять порядком, в котором выполняется написанное, и организовывать код в функции с ясным интерфейсом. Если это так, его предмет не язык, а само программирование: придумать алгоритм, представить данные, разложить задачу на части и рассуждать о корректности и стоимости написанного. Поэтому он называется «Основы программирования», и поэтому каждая конструкция C++ появляется вместе с задачей, которую она решает, и со способом рассуждать о ней, и никогда в виде каталога синтаксиса.

Следовательно, C++ есть инструмент курса, а не его цель. Однако инструмент не безразличен к тому, чему с его помощью учатся, и выбор именно этого, который на первый взгляд кажется самым трудным, сделан сознательно; следующий параграф его обосновывает. Стоит, однако, заранее назвать его самое заметное следствие: каждый урок итогов раздела содержит постоянный параграф «Что верно для любого языка», который отделяет изученное от его формы в C++ и говорит, как то же выражается, или что меняется, в других языках. Отсюда следует, что студент, закончивший курс, будет уметь не только писать на C++, но и узнавать в любом языке те же идеи в другом облачении.

Почему C++, а не более дружелюбный язык

Есть языки с более пологой кривой обучения, например Python, в которых первая полезная программа пишется в несколько строк, без объявления типов, без компиляции и без вопроса о том, где живут данные. Если бы целью было поскорее писать работающие программы, это был бы подходящий путь. Цель этого курса, напротив, состоит в том, чтобы понимать, что делает программа, а для этого нужен язык, который не скрывает машину. C++ заставляет видеть шесть вещей, которые более удобные языки решают молча:

  • Типы. Каждый элемент данных объявляется с типом, который определяет, какие значения он может принимать, сколько места занимает и какие операции допускает; компилятор отвергает то, что не согласуется, ещё до того, как что-либо будет выполнено.
  • Компиляция. Написание, компиляция, компоновка и выполнение суть разные моменты, и каждая ошибка принадлежит одному из них; знать, какому именно, значит наполовину её исправить.
  • Память. Переменная есть именованная ячейка памяти со временем жизни и областью видимости; ссылка есть другое имя для той же ячейки, а ссылка на ячейку, которой уже не существует, есть настоящая ошибка.
  • Стоимость копирования. Передача вектора в функцию может скопировать его целиком; выбор между передачей по значению и по ссылке есть решение того, кто программирует, а не машины.
  • Пределы чисел. 32-битное целое не превосходит 2 147 483 6472\,147\,483\,647, а число с плавающей точкой не хранит 0,10{,}1 точно.
  • Неопределённое поведение. То, что стандарт не определяет (выход за границы массива, переполнение знакового целого, чтение неинициализированной переменной), даёт не гарантированную ошибку, а что угодно; поэтому курс всегда компилирует с предупреждениями и санитайзерами.

Следующий пример показывает это в малом. Программа вычисляет факториалы от 11 до 1515 в переменной типа int. В Python, где целые числа растут без ограничений, то же вычисление без возражений напечатало бы 13!=6 227 020 80013! = 6\,227\,020\,800; в C++ произведение 12!12! на 1313 не помещается в int, и санитайзер неопределённого поведения, с которым курс компилирует все свои примеры, останавливает программу точно на нужной строке:

Программа на C++
#include <print>​int main(){    int factorial{1};    for (int n{1}; n <= 15; ++n) {        factorial = factorial * n;   // поместится ли произведение в int?        std::println("{}! = {}", n, factorial);    }}
Вывод
1! = 12! = 23! = 64! = 245! = 1206! = 7207! = 50408! = 403209! = 36288010! = 362880011! = 3991680012! = 479001600main.cpp:7:31: runtime error: signed integer overflow: 479001600 * 13 cannot be represented in type 'int'

Программа завершилась с кодом 1.

Если это так, язык заставил задать вопрос, который другой язык скрыл бы: сколько помещается в выбранный тип и что делать, когда не помещается. Ни один из двух ответов не лучше в отвлечённом смысле; важно знать, что вопрос существует. Урок Целые числа и их представление на него отвечает, а параграф о неопределённом поведении в каждом уроке итогов к нему возвращается.

Отсюда следует асимметрия, которая и есть глубинная причина выбора. Тот, кто изучает основы на C++, затем понимает, что делают за него более удобные языки: сборщик мусора, целые числа без ограничений, контроль индексов, молчаливое преобразование типов. Тот же, кто сначала учится на одном из них, оставляет эти слои невидимыми и спотыкается о них позже, когда настоящая программа ломается по одной из причин, которые язык от него скрыл. Следовательно, путь, который выбирает курс, труднее вначале, но лучше переносится: то, что изучено здесь, верно для любого языка, а то, что изучено на языке, который это скрывает, не всегда верно здесь.

Однако C++ есть большой язык, и курс основ должен устоять перед искушением преподать его целиком. Этот первый курс ограничивается структурным программированием (данные, выражения, управление потоком, последовательности, текст и функции) в стандарте C++23 в его современных формах: вывод через std::println, последовательности через std::array и std::vector, текст через std::string, алгоритмы библиотеки в их форме с диапазонами. Он оставляет для курса «Программирование на C++ II» то, что требует этой основы: указатели и динамическую память, классы с методами, собственные шаблоны, анонимные функции и исключения.

Адресаты и предварительные знания

Курс адресован тем, кто никогда не программировал, на любой образовательной программе и с первого года: студентам информатики, которым нужен прочный фундамент перед курсами структур данных и систем, студентам инженерных и естественнонаучных специальностей, которым нужно вычислять на компьютере, не доверяя ему вслепую, и студентам-математикам, которые хотят увидеть свои алгоритмы превращёнными в выполняющиеся программы.

Это курс уровня 1, без предварительных требований и самодостаточный. Он предполагает лишь две вещи: школьную арифметику с целочисленным делением и его остатком и элементарное обращение с компьютером (создать папку, сохранить файл, открыть программу). То, что он использует из двоичного представления, из рассуждения по случаям, из логики условий или из индукции, он преподаёт там, где это нужно: двоичную систему счисления в уроке о целых числах; законы де Моргана в уроке об отношениях и логике; индукцию в уроке о рекурсии. Ссылки на другие курсы сайта, встречающиеся в уроках, суть, следовательно, углубления, а не требования.

Куда он ведёт

Из сказанного следует место курса на сайте. У него нет предварительных требований, и он входит в цели двух образовательных программ, «Информатика и вычисления» и «Инженерия», потому что в обеих программирование есть одновременно инструмент и предмет. Поэтому его стоит проходить одним из первых курсов этих программ вместе с курсами «LaTeX для академического письма» и «Математическая логика I: логика высказываний».

У него есть дополнительный курс, который можно проходить до него, параллельно или после. Курс «Математическая логика I: логика высказываний» изучает как теорию то, что здесь используется на практике: составные условия, таблицы истинности и законы де Моргана из раздела 3, а также идею доказательства того, что процедура корректна, которая в этом курсе принимает форму инварианта цикла и индукции по рекурсии. Курс «Алгебра и геометрия I», со своей стороны, доказывает те результаты о числах, которые курс программирует: евклидово деление, алгоритм Евклида, системы счисления и схему Горнера. А курс «LaTeX для академического письма» в своём уроке Совместное написание текстов преподаёт контроль версий с Git, которым этот курс советует пользоваться с первой программы.

Однако курс сознательно оставляет в стороне то, что требует его основы. Его предусмотренное продолжение есть курс «Программирование на C++ II», который рассмотрит указатели и динамическую память, классы и объекты, шаблоны, анонимные функции, исключения и стандартную библиотеку в глубину. Откуда следует, что курс не есть первая часть руководства по C++, а законченный курс основ, на котором можно остановиться; тот же, кто продолжит, найдёт в курсе II ту же дисциплину, применённую к программам, размер которых уже не позволяет без неё обойтись.

Общие учебные цели

Следующие цели описывают, что должен уметь студент, завершивший курс. Каждая сформулирована как наблюдаемое действие, так что её достижение можно удостоверить конкретным заданием; каждый содержательный урок в свою очередь заявляет собственные цели, служащие одной или нескольким общим, а заключительный урок показывает, в каких разделах и уроках прорабатывается каждая. Семь целей пронумерованы, чтобы на них можно было ссылаться.

  1. Цель 1. Решать задачи с помощью программ: специфицировать, что поступает на вход и что получается на выходе, разрабатывать алгоритм, выполнять его вручную, а также писать, компилировать, запускать и отлаживать его, истолковывая сообщения и предупреждения компилятора.
  2. Цель 2. Выбирать подходящий тип для каждого элемента данных и рассуждать о пределах его представления: диапазоне значений целых чисел, переполнении, округлении чисел с плавающей точкой и кодировке текста.
  3. Цель 3. Строить правильные выражения, зная приоритет, преобразования типов, побочные эффекты и то, что стандарт оставляет неопределённым.
  4. Цель 4. Проектировать управление потоком с правильными ветвлениями и циклами, которые завершаются, и обосновывать их корректность инвариантом.
  5. Цель 5. Разбивать задачу на функции с ясными интерфейсами, выбирать способ передачи параметров и решать задачи с помощью рекурсии.
  6. Цель 6. Представлять и обрабатывать составные данные (последовательности, таблицы, записи, текст и файлы) с помощью базовых алгоритмов и стандартной библиотеки, оценивая их сложность.
  7. Цель 7. Организовывать более крупные программы в нескольких файлах, специфицировать их предусловием и постусловием, систематически их тестировать и аргументировать их корректность.

У пяти целей есть свой раздел: у цели 2 раздел о данных и типах; у цели 3 раздел о выражениях; у цели 4 раздел об управлении потоком; у цели 5 раздел о функциях; у цели 6 раздел о составных данных, который продолжается в разделе о тексте и файлах, где цель 2 появляется снова вместе с кодировкой UTF-8. Две другие цели, напротив, проходят через весь курс. Для цели 1 главными являются первый раздел, который учит циклу работы «специфицировать, написать, скомпилировать, запустить», и урок об отладке из последнего; но она упражняется во всех разделах, потому что каждая программа курса компилируется, запускается и исправляется. Для цели 7 главным является последний раздел, о более крупных программах; но она начинается в руководстве по стилю из первого, продолжается в константах второго, обретает форму в контрактах пятого, которые с тех пор сопровождают каждую функцию, и в инвариантах четвёртого и шестого. Отсюда следует, что их достижение нельзя приписать одному разделу и что их стоит развивать на протяжении всего курса.

Содержание

Курс разделён на десять разделов: это знакомство, восемь содержательных разделов и завершение. Каждый содержательный раздел открывается вводным уроком, который представляет его уроки, то, что раздел считает известным, и задачу, которую он решает, и начинается с входной проверки; закрывается он уроком итогов, который восстанавливает цепочку понятий раздела, отделяет то, что верно для любого языка, от его формы в C++, сводит воедино неопределённое поведение раздела, повторяет в итоговой проверке навыки входной и предлагает задачи повышенной трудности, более сложные, чем задачи уроков.

Раздел 0. Знакомство с курсом

Он состоит из этого урока, в котором определены цель, выбор языка, общие цели, содержание, трудоёмкость и устройство курса.

Раздел 1. Первые шаги

Его цель состоит в том, чтобы провести студента от задачи, сформулированной словами, до программы, которая компилируется, запускается, считывает данные и выводит результаты, так чтобы он знал, что сделал по пути каждый инструмент. Он начинается без кода, потому что алгоритм предшествует программе; в конце студент знает о C++ ещё мало, но знает, как с ним работать.

  1. Первые шаги в C++: введение.
  2. От задачи к алгоритму: спецификация входных и выходных данных, пронумерованные шаги, трассировка вручную, следование, ветвление и повторение.
  3. Программы, компиляторы и C++: препроцессирование, компиляция и компоновка; компилируемые языки в сравнении с интерпретируемыми; стандарты языка.
  4. Установка среды в Windows, macOS и Linux: три полных руководства, компилятор, Visual Studio Code и его расширение для C++, контрольный запуск и частые проблемы.
  5. Компиляция, запуск и настройка: опции курса, ошибки и предупреждения, санитайзеры, задача сборки редактора и кодировка консоли.
  6. Анатомия программы: включение заголовков, main, инструкции, блоки, комментарии и руководство по стилю курса.
  7. Ввод, вывод и ошибки: std::cin, std::println и спецификации формата, некорректный ввод и классификация ошибок.
  8. Первые шаги в C++: итоги и задачи повышенной трудности.

Раздел 2. Данные и типы

Его цель состоит в том, чтобы показать, что всякий элемент данных живёт в памяти с типом, который определяет, какие значения он может принимать, сколько места занимает и какие операции допускает, и что число в программе не есть число математики, а представление с пределами.

  1. Данные и типы: введение.
  2. Переменные и память: объявление и инициализация фигурными скобками, присваивание, трассировка состояния, имена и неинициализированная переменная.
  3. Целые числа и их представление: основания 2 и 16, диапазон значений каждого типа, дополнительный код и переполнение знаковых и беззнаковых чисел.
  4. Числа с плавающей точкой: почему 0,10{,}1 не представимо точно, накопление погрешностей округления, сравнение с допуском, бесконечность и не-число (NaN).
  5. Символы, логические значения, преобразования и константы: код символа, неявные и явные преобразования, запрет преобразований, теряющих информацию, const, constexpr и auto.
  6. Данные и типы: итоги и задачи повышенной трудности.

Раздел 3. Выражения и операторы

Его цель состоит в том, чтобы научить строить выражения, значение которых можно предсказать с уверенностью: какая операция выполняется первой, какого типа результат, какие у выражения побочные эффекты и какие выражения лишены смысла. В разделе ещё нет ни ветвлений, ни циклов: условия вычисляются и печатаются.

  1. Выражения и операторы: введение.
  2. Арифметические операции: частное и остаток для отрицательных чисел, дерево выражения, приоритет, ассоциативность и тип результата.
  3. Отношения и логика: составные условия, их отрицание по де Моргану, вычисление по короткой схеме и ошибка 1 < x < 5.
  4. Присваивание, побочные эффекты и неопределённое поведение: значение и эффект выражения, инкременты, порядок вычисления и каталог того, что стандарт не определяет.
  5. Биты и математическая библиотека: побитовые операторы, сдвиги, маски, <cmath> и константы из <numbers>.
  6. Выражения и операторы: итоги и задачи повышенной трудности.

Раздел 4. Управление потоком

Его цель состоит в том, чтобы научить управлять порядком выполнения с помощью ветвлений и циклов и рассуждать об их корректности: чтобы каждый случай был обработан, чтобы каждый цикл делал то, что должен, и чтобы он завершался. Он воплощает то, что возвестил первый урок курса: ветвление и повторение, теперь записанные и обоснованные.

  1. Управление потоком: введение.
  2. Ветвления: цепочки и вложения if и else, непересекающиеся и исчерпывающие случаи, switch с enum class и его ловушки.
  3. Циклы: while, do и for, трассировка цикла, переход от одной формы к другой и вложенные циклы.
  4. Проектирование правильных циклов: шаблоны счётчика, накопителя, максимума и сигнального значения, инвариант, завершение и первое знакомство с отладчиком.
  5. Численные алгоритмы: тест на простоту, алгоритм Евклида, перевод в другую систему счисления, схема Горнера и метод бисекции, каждый со своим обоснованием.
  6. Управление потоком: итоги и задачи повышенной трудности.

Раздел 5. Функции

Его цель состоит в том, чтобы научить разбивать задачу на именованные части, каждая со своим интерфейсом и контрактом, и решать задачи с помощью рекурсии. Он превращает длинные программы раздела 4 в программы, составленные из частей, которые обосновываются по одной.

  1. Функции: введение.
  2. Функции: объявление, определение и вызов, стек вызовов, предусловие и постусловие, функция, которая не возвращает значения.
  3. Параметры и область видимости: передача по значению, по ссылке и по константной ссылке, несколько результатов через структурные привязки, область видимости и время жизни переменных.
  4. Проектирование с помощью функций: пошаговая детализация, интерфейс прежде тела, правило «одна функция, одна задача» и перегрузка.
  5. Рекурсия: базовый и рекурсивный случаи, корректность и завершение по индукции, стек и стоимость рекурсии.
  6. Функции: итоги и задачи повышенной трудности.

Раздел 6. Составные данные

Его цель состоит в том, чтобы научить представлять коллекции данных и обрабатывать их базовыми алгоритмами, написанными вручную и взятыми из библиотеки, оценивая их сложность. Он ведёт от отдельного элемента данных к коллекции и от вопроса «правильно ли?» к вопросу «сколько это стоит?».

  1. Составные данные: введение.
  2. Массивы: std::array, обход по индексам и for по диапазону, индекс в пределах границ и массивы в стиле C для сравнения.
  3. Векторы: std::vector, чтение ряда неизвестной длины, накопление, фильтрация и преобразование, размер и ёмкость.
  4. Сложность алгоритма: подсчёт доминирующих операций, порядки роста и измерение времени.
  5. Поиск и сортировка: линейный и двоичный поиск, сортировка выбором и вставками с их инвариантами и алгоритмы библиотеки.
  6. Таблицы: двумерные таблицы, их обход по строкам и по столбцам, транспонирование, произведение и таблица в плоском векторе.
  7. Записи: struct, назначенные инициализаторы, записи как параметры и сортировка по полю.
  8. Составные данные: итоги и задачи повышенной трудности.

Раздел 7. Текст и файлы

Его цель состоит в том, чтобы научить обращаться с текстом как с данными и добиться того, чтобы программа читала и записывала файлы, не ломаясь на некорректном вводе. В конце студент может писать программы, которые обрабатывают реальные данные, с диакритикой и ошибками.

  1. Текст и файлы: введение.
  2. Строки: std::string и его операции, npos, представления std::string_view и строки в стиле C для сравнения.
  3. Текст не сводится к ASCII: кодировка UTF-8, байты и символы и операции, которые портят текст.
  4. Надёжное чтение ввода: std::getline, состояние потока, восстановление после неверного значения и контроль корректности с помощью std::from_chars.
  5. Текстовые файлы: открытие, построчное чтение, запись, дозапись и обработка файла значений, разделённых запятыми.
  6. Текст и файлы: итоги и задачи повышенной трудности.

Раздел 8. Более крупные программы

Его цель состоит в том, чтобы научить организовывать, обосновывать и отлаживать программы, которые уже не помещаются ни в одном файле, ни в голове. Он замыкает круг, открытый первым уроком: специфицировать, построить, удостовериться; его урок итогов заканчивается итоговым проектом, который объединяет весь курс.

  1. Более крупные программы: введение.
  2. Несколько файлов и собственные библиотеки: заголовочный файл и реализация, защита от повторного включения, правило одного определения и раздельная компиляция.
  3. Тестирование программ: тестовые случаи, выведенные из спецификации, автоматические тесты, assert и static_assert.
  4. Отладка программ: отладчик Visual Studio Code, точки останова, стек вызовов и систематическая стратегия поиска ошибки.
  5. Более крупные программы: итоги и задачи повышенной трудности.

Раздел 9. Завершение курса

Он состоит из урока Итоговый обзор курса «Программирование на C++ I», который напоминает семь общих целей, показывает на рисунке, в каких разделах и уроках прорабатывается каждая, сводит по целям первую попытку итоговых проверок и указывает, куда продолжается обучение.

Трудоёмкость и кредиты

Трудоёмкость курса измеряется не часами лекций, которых в асинхронном курсе нет, а всей работой студента: чтением, набором и компиляцией примеров, решением задач и повторением. Оценка следует одному и тому же методу во всех курсах сайта: она учитывает время чтения текста каждого урока, включая код его примеров, из расчёта сто слов в минуту, двадцать минут на решённую задачу, тридцать пять на задачу для самостоятельного решения, сорок пять на задачу повышенной трудности и двенадцать на входную или итоговую проверку, и добавляет запас в одну десятую на повторение.

Результаты

Уроки курса содержат в совокупности 105105 решённых задач, 105105 задач для самостоятельного решения и 5252 задачи повышенной трудности, а также шестнадцать проверок разделов и семьдесят коротких упражнений, которые сайт проверяет мгновенно. Оценка трудоёмкости по разделам, округлённая до часа, такова:

  • Раздел 0, знакомство с курсом: 11 час.
  • Раздел 1, первые шаги: 2828 часов.
  • Раздел 2, данные и типы: 2222 часа.
  • Раздел 3, выражения и операторы: 2222 часа.
  • Раздел 4, управление потоком: 2222 часа.
  • Раздел 5, функции: 2222 часа.
  • Раздел 6, составные данные: 3030 часов.
  • Раздел 7, текст и файлы: 2121 час.
  • Раздел 8, более крупные программы: 2020 часов.
  • Раздел 9, завершение курса: менее 11 часа.

Всего получается около 188188 часов работы студента. Эту цифру стоит понимать так, как она есть: как оценку для студента, который набирает и компилирует все примеры и решает все задачи, в том числе повышенной трудности. Тот, кто уже программировал на другом языке, потратит на первые разделы заметно меньше; тому, кто никогда не устанавливал программу и не читал сообщение об ошибке, может понадобиться больше, особенно в разделе 1, который вместе с разделом о составных данных является и самым объёмным.

Однако эта цифра номинальна: именно она определяет кредиты курса, и она предполагает чтение в постоянном темпе и три четверти часа на задачу повышенной трудности. Пока она не измерена на реальных студентах, её стоит сопровождать реалистичным диапазоном. Если бы чтение шло вдвое медленнее предположенного темпа, как бывает, когда каждый пример набирают вручную, компилируют и изменяют, чтобы увидеть, что меняется, и каждая задача повышенной трудности требовала семидесяти пяти минут вместо сорока пяти, тот же метод дал бы около 250250 часов. Следовательно, ожидаемая трудоёмкость лежит в пределах от примерно 188188 до примерно 250250 часов. Верхняя граница есть осторожная оценка сверху, а не измерение; диапазон будет уточнён, когда станет известно время, которое тратят изучающие курс, а до тех пор кредиты рассчитываются по номинальной цифре.

Рекомендуемый темп

Чтобы назвать темпы, этот план использует тот же критерий, что и другие учебные планы сайта: полная занятость соответствует учебному году в 6060 кредитов, примерно 16501650 часам, распределённым примерно на сорок недель, то есть примерно 4040 часам в неделю, а неполная занятость составляет половину, примерно 2020 часов в неделю. Отсюда следует рекомендуемый темп в один семестр из шестнадцати недель, примерно по 1212 часов занятий в неделю, немногим меньше трети полной занятости; с учётом реалистичного диапазона от 1212 до 1616 часов в неделю. Этот семестр можно разделить с курсом «Математическая логика I: логика высказываний», таблицы истинности и идея доказательства которого узнаются в условиях и инвариантах этого курса.

Интенсивный темп, примерно 2020 часов в неделю, то есть неполная занятость, позволяет пройти курс за девять или десять недель. Более свободный темп, примерно 66 часов в неделю, растягивает его на два семестра, которые делятся почти пополам: разделы с 0 по 4, от алгоритма до управления потоком, занимают около 9595 часа, а разделы с 5 по 9, от функций до итогового проекта, около 9393. Разрез естественный: к концу раздела 4 студент уже пишет законченные программы в одной функции main, а вторая половина учит его их организовывать.

Кредиты

Кредиты этого сайта измеряют всю работу, которой курс требует от того, кто его изучает (чтение, задачи, в том числе повышенной трудности, и повторение), а не часы занятий. Один кредит соответствует примерно 27,527{,}5 часа работы, а год при полной занятости равен 6060 кредитам, то есть примерно 16501650 часам; это тот же критерий, по которому предыдущий пункт называет темпы занятий.

Однако если разделить 188188 оценочных часов на 27,527{,}5, получается 6,8, что округляется до 77. Если допустить для кредита диапазон от 2525 до 3030 часов работы, то число CC кредитов курса ограничено пределами от 6,3 до 7,5:

(18830)≤C≤(18825)\left( \frac{188}{30} \right) \leq C \leq \left( \frac{188}{25} \right)

Следовательно, у курса 77 кредитов, немногим больше девятой части года полной занятости. Эта цифра не недосмотр: курс длится столько, сколько нужно, чтобы преподать основы, и его не урезали, чтобы уложиться в круглое число кредитов.

Их польза практическая: они позволяют сравнивать между собой нагрузку курсов сайта и решать, сколько из них стоит изучать одновременно, не превышая доступного времени. Например, при неполной занятости в течение семестра из шестнадцати недель, то есть примерно за 320320 часов, можно взяться за курсы, которые в сумме составляют около 1212 кредитов; при полной занятости в тот же семестр около 2323.

Эти кредиты являются собственной мерой сайта для планирования учёбы: они не соответствуют никакой официальной системе кредитов и не означают академического признания со стороны какого-либо учреждения.

Устройство курса

Форма обучения

Курс асинхронный, онлайн и в собственном темпе: у него нет дат, занятий в реальном времени и сроков, и каждый студент продвигается с той скоростью, которую позволяют его подготовка и его время. Курс будет предложен на четырнадцати языках сайта. Идентификаторы в программах написаны по-английски и одинаковы во всех версиях; комментарии же и сообщения, которые показывают программы, написаны на языке страницы, так что каждая версия компилируется и выполняется со своим собственным выводом. Уроки указывают, какие другие уроки стоит изучить заранее, так что рекомендуемый порядок задан явно.

Устройство урока

Все содержательные уроки устроены одинаково. Они начинаются с резюме и со списка собственных учебных целей, сформулированных как наблюдаемые действия. Затем следует изложение, ядро которого составляют программы. Каждая законченная программа курса перед сборкой сайта была скомпилирована по стандарту C++23 со всеми включёнными предупреждениями и с санитайзерами ошибок памяти и неопределённого поведения и выполнена; то, что публикуется под кодом, есть её настоящий вывод, или настоящие сообщения компилятора, когда урок учит ошибке или предупреждению, или настоящий отчёт санитайзера, когда программа совершает неопределённое поведение. Если это так, ни один вывод в курсе не написан вручную: при сборке сайт удостоверяется, что у каждой программы сохранён вывод её текущей версии, и не был бы опубликован, если бы у какой-нибудь его не хватало. Другие рисунки показывают по шагам то, о чём рассуждает текст: ячейки памяти с их именем и значением, стек вызовов, ход цикла, биты числа.

Каждый урок завершают три решённые задачи с пошаговым решением и три задачи для самостоятельного решения, каждая со свёрнутой подсказкой и затем ответом, иногда сопровождаемым подробным решением, которое стоит открывать только после того, как написано собственное. Задачи требуют идеи, а никогда не механического повторения примера, и используют только то, что изучено к этому уроку. Каждый урок содержит, кроме того, два коротких упражнения, которые сайт проверяет мгновенно: в одном нужно предсказать точный вывод программы, в другом дополнить или исправить фрагмент. Стоит знать, что такая сверка делает и чего не делает: сайт не компилирует код студента, а с помощью шаблонов удостоверяется, что существенное присутствует и что искомая ошибка исчезла. Следовательно, упражнение, принятое сайтом, ещё нужно скомпилировать и выполнить на собственном компьютере, который один есть окончательный судья.

Вводные уроки и уроки итогов устроены по-своему. Введение представляет раздел, его уроки и связующую их нить, то, что раздел считает известным, и задачу, которую он решает, и содержит входную проверку. Урок итогов восстанавливает цепочку понятий раздела с уроком, в котором устанавливается каждое звено, и тем, на что каждое опирается; он содержит два постоянных параграфа, «Что верно для любого языка» и «Неопределённое поведение в разделе», итоговую проверку и шесть задач повышенной трудности (десять в последнем разделе, итоговый проект которого разбит на этапы), каждая с подсказкой, ответом и, когда рассуждение того заслуживает, свёрнутым подробным решением.

Озвучивание текста

Каждый урок можно прослушать вслух с помощью озвучивания сайта, которое использует голоса самого устройства: текст читается так, как он написан, процитированный в нём код произносится осмысленно, а примеры кода объявляются, но не читаются символ за символом. Это позволяет повторять изложение вдали от экрана; программы же всегда требуют редактора.

Проверка

Курс не ставит оценок: оценивание в нём формирующее и опирается на самопроверку. Задачи для самостоятельного решения позволяют удостовериться в достижении целей каждого урока, а задачи повышенной трудности в достижении целей каждого раздела; во всех них последний судья есть компилятор с его предупреждениями и санитайзерами, потому что программа, которая компилируется без предупреждений, проходит свои тестовые случаи и не будит ни одного санитайзера, есть свидетельство, не допускающее споров, хотя, как учит урок о тестировании, не доказательство.

К этому добавляются проверки разделов. Входная проверка во вводном уроке есть короткая самопроверка, которая измеряет исходный уровень; итоговая проверка в уроке итогов перед задачами повышенной трудности измеряет достигнутое и сравнивается с входной. Обе оцениваются прямо в браузере: каждый ответ получает объяснение, каждая типичная ошибка замечание, которое её называет, а каждый вопрос отсылает к уроку, который стоит повторить. Ни одна из них не ставит оценок и не выдаёт сертификатов, а их результаты хранятся только в браузере студента; подтверждение приобретённых навыков будет проводиться позже на другой платформе. Итоговый обзор сводит первую попытку каждой итоговой проверки в профиль по общим целям.

Ассистенты на основе искусственного интеллекта

Сегодня существуют ассистенты на основе искусственного интеллекта, способные за секунды написать решение почти любой задачи этого курса. Стоит ясно сказать, какое место им подобает, и причина та же, что оправдывает всё остальное: курс тренирует не производство программ, а способность продумать задачу до того, чтобы превратить её в правильную программу. Если это так, то поручить упражнение ассистенту значит уничтожить упражнение, точно так же, как взгляд на ответ задачи до попытки превращает её в чтение. Полученная программа может быть правильной; тот же, кто её сдаёт, не научился её писать и не сумеет этого сделать, когда ассистент ошибётся, а это как раз тот момент, когда умение нужнее всего.

Отсюда следует правило пользования, а не запрет. Ассистент пригоден, чтобы объяснить непонятное сообщение об ошибке, предложить тестовые случаи, о которых не подумали, сравнить уже законченное собственное решение с другим или прояснить идею изложения под другим углом. Он не пригоден для того, чтобы писать упражнения, задачи или задачи повышенной трудности. И во всех случаях действует принцип, верный для любого чужого кода, откуда бы он ни пришёл, от ассистента, с форума или от однокурсника: его читают, тестируют и понимают, прежде чем принять. Ассистент может выдать код, который компилируется и кажется правильным, но совершает неопределённое поведение или использует устаревшую форму языка; студент, прошедший курс, сумеет это распознать, а тот, кто перепоручил курс другому, нет.

Как изучать этот курс

Курс рассчитан на изучение по порядку. Каждый раздел предполагает предыдущие: выражения пишутся с типами второго раздела, циклы с выражениями третьего, функции заключают в себе циклы четвёртого, а составные данные обрабатываются функциями; пропуск урока рано или поздно вынуждает к нему вернуться.

Главное правило одно: набирать и компилировать каждый пример. Программированию учатся не чтением, а письмом, компиляцией и чтением того, что отвечает компилятор. Поэтому студент устанавливает собственную среду по уроку Установка среды в Windows, macOS и Linux и воспроизводит каждую программу в своём Visual Studio Code с опциями, которые учит задавать урок Компиляция, запуск и настройка; тот, кто пользуется компьютером, на который нельзя устанавливать программы, может временно воспользоваться онлайн-компилятором, который описан в уроке об установке. Примеры стоит набирать вручную, а не копировать, потому что рука удерживает то, что глаз пропускает; вывод каждой программы стоит предсказывать до её запуска, потому что разница между предсказанным и полученным есть ровно то, чему ещё предстоит научиться; и каждое предупреждение компилятора стоит читать как вопрос, заслуживающий ответа, а не как шум, который терпят. После того как программа заработала, её стоит ещё и намеренно изменить (стереть точку с запятой, сменить тип, выйти за пределы вектора) и прочитать, что скажут компилятор или санитайзер, потому что каждая вызванная ошибка есть ошибка, которую потом узнают.

В каждом разделе есть две контрольные точки. Входная проверка во вводном уроке есть точка отсчёта: на неё стоит ответить до изучения раздела, ни во что не заглядывая, и записать уроки, к которым отсылают ошибки. Итоговая проверка в уроке итогов перед задачами повышенной трудности есть сопоставление: на неё отвечают по окончании уроков, тоже ни во что не заглядывая, и сравнивают с входной. В обеих считается первая попытка, именно она сравнивается и именно она входит в профиль итогового обзора. В задачах для самостоятельного решения и в задачах повышенной трудности подсказку стоит открывать лишь тогда, когда попытка исчерпана, а ответ только в конце, чтобы сверить. Задача, ответ которой подсмотрели до компиляции собственного решения, не решена, а прочитана.

Контроль версий

Наконец, работу по курсу стоит сохранять в системе контроля версий с первой программы. Git записывает каждое состояние рабочей папки, позволяет вернуться к любому из них и показывает, что изменилось между двумя; для того, кто программирует, это значит возможность экспериментировать без страха потерять то, что работало, и находить, сравнивая версии, изменение, которое внесло ошибку, что переносит на историю программы бисекцию, которой учит урок об отладке. Курс не преподаёт Git, потому что этим уже занимается другой курс сайта: урок Совместное написание текстов курса «LaTeX для академического письма» объясняет минимальный рабочий процесс, какие файлы сохраняются и какие исключаются и как разрешается конфликт. То, что там верно для исходных текстов документа, точно так же верно для исходного кода программы; достаточно исключить вместо вспомогательных файлов LaTeX исполняемые и объектные файлы, которые компиляция создаёт заново.