В. А. Матюхин “Преподавание программирования с использованием системы автоматической проверки решений”

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

Компьютер — действующее лицо педагогического процесса

Программирование — уникальный предмет. Дело в том, что при изучении почти любого другого предмета учебный процесс строится на
взаимодействии ученика и учителя. При изучении же программирования в этот процесс включается еще одно действующее лицо — компьютер. Что фактически требуется от школьника на первых порах изучения программирования? Взять задачу, которую он отлично понимает как нужно решать без компьютера (например, задачу поиска минимума среди набора чисел), после чего попытаться осознать, что же он (школьник) делает, решая данную задачу, и сформулировать это словами, а затем перевести этот алгоритм на язык машины и получить программу. И вот здесь в действие вступает компьютер.

Во-первых, если в программе допущены синтаксические ошибки, то компилятор сразу на это укажет. Во-вторых, когда все синтаксические ошибки исправлены, часто программа все равно не работает. И опять же ребенок может увидеть это сам, запустив программу на каком-нибудь примере.
Очень важно, что ребенок имеет возможность увидеть, а зачастую и исправить ошибки без всякого вмешательства учителя. Во-первых, в этом случае значительно снижается время получения «обратной связи» школьником (учитель просто физически не имеет возможности с такой скоростью просматривать работы школьников и указывать на ошибки — как правило, на других предметах школьники сначала в течение какого-то времени выполняют работу, потом сдают и на следующем уроке получают проверенную работу). Во-вторых, когда на ошибку указывает не товарищ, не учитель, а бездушное устройство, у школьника исчезает страх ошибку совершить. Пользуясь тем, что можно быстро что-нибудь исправить, снова запустить программу и очень быстро узнать, что и этот вариант неверен (или, наоборот, верен), школьник довольно активно пробует разные варианты. Тем самым, он учится самостоятельно преодолевать проблемы,
искать источники ошибок и т. д.

Роль отладчика

Еще один очень полезный инструмент при изучении программирования — отладчик. С его помощью школьник может выполнять программу по шагам и отслеживать значения переменных. Фактически отладчик позволяет школьнику сравнить, что должно происходить при выполнении программы по мнению школьника и что происходит на самом деле.
Мне очень нравится сравнение роли отладчика с зеркалом. Когда человек учится танцевать, он делает это перед зеркалом, чтобы видеть, что у него получается. Когда человек учится программировать, фактически он учится излагать свои мысли на языке программирования — абсолютно строгом, формальном языке. Программа — это изложение мыслей школьника о том, что нужно делать для решения задачи. Отладчик позволяет увидеть, где то, что написано в программе, не совпало с тем, что он хотел написать.
Пользоваться отладчиком не всегда просто. Но научить школьников им пользоваться очень важно. Часто школьнику значительно проще
обратиться к учителю с вопросом «а где у меня ошибка?», и бывает, что учителю достаточно одного взгляда на программу, чтобы эту ошибку увидеть и указать на нее школьнику. Однако обучающий эффект будет значительно больше, если школьник найдет эту ошибку сам.

Система автоматической проверки

Итак, школьник добился того, что программа работает на каком-то примере. Вроде бы задача решена. Это далеко не всегда так. В задаче
могут быть хитрые случаи, специально спрятанные «грабли», или программа написана так, что работает лишь в случаях специального типа, но именно на таком примере и проверил свое решение школьник.
Теперь, по идее, к школьнику должен подойти учитель и проверить его программу. Запустить ее несколько раз, предложив ей разные входные данные. Однако, во-первых, учитель физически не может запустить программу больше чем на 2-3 примерах, и может так получиться, что и эти тесты программа пройдет. Во-вторых, входные данные в задаче могут быть достаточно большими, и учителю потребуется довольно много времени на то, чтобы их набить (а ведь надо набить без ошибок!). В-третьих, ответ на этих примерах может быть неоднозначным, и потребуется еще время на то, чтобы понять, правильный ответ вывела программа школьника или нет.
На самом деле, эту проверку можно переложить с учителя на компьютер.
Компьютер сам может давать программе школьника наборы входных данных («подсовывая» ей разные входные файлы или эмулируя ввод с клавиатуры). Сам запускать программу школьника и анализировать выданный ею ответ.

Это и есть автоматическая проверка решения. При этом система автоматической проверки — не универсальная программа, которая может
проверять решение любой задачи. Это программа, которая умеет «подсовывать» тесты (т.е. наборы входных данных), запускать решение, анализировать результат и, быть может, как-то вести протокол, и поддерживать таблицу результатов. Чтобы система могла проверять некоторую задачу, нужно, чтобы были подготовлены тесты специально для этой задачи.
Причем, в отличие от учителя, компьютер может проверить решение очень качественно — за минуту можно прогнать до сотни разных тестов (в том числе и с достаточно большими по объему входными данными).
Если все они прошли, то программа признается правильной. Если какой-то тест не прошел — то, очевидно, программа содержит ошибку.
Дальнейшая тактика может быть разной. Школьнику может выдаваться этот тест (т.е. набор входных данных), чтобы, пользуясь отладчиком, школьник мог найти и исправить ошибку. Так правильно поступать на первых порах изучения программирования. А можно не выдавать тест, а лишь сообщить о том, что в программе имеется ошибка (часто принято сообщать номер теста, чтобы школьник мог сравнить, к чему привели его исправления по сравнению с предыдущей попыткой сдать задачу — программа стала работать хуже и проходить меньше тестов, или наоборот, ему удалось исправить ошибку, из-за которой программа не работала в прошлый раз,
и теперь нужно искать еще какие-то ошибки). В такой ситуации школьник учится анализировать задачу, выделять из нее ключевые моменты, понимать, что в его программе может приводить к ошибке. Это очень непростое, но очень важное умение.

Требования к программам

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

Авторитет системы

Как уже говорилось, чтобы можно было проверять решения некоторой задачи, должны быть подготовлены тесты именно для этой задачи. Кроме того, когда ответ в задаче неоднозначен, часто требуется написание программы, которая по входным данным и выданному проверяемой программой результату устанавливает его правильность. Это непростая работа, которая требует специальных умений. В последнее время на сайтах (преимущественно посвященных олимпиадам по программированию, так как технология автоматической проверки решений изначально применялась именно на олимпиадах по программированию и лишь потом была
перенесена в учебный процесс) накоплена довольно большая база задач с тестами, которые можно использовать. Это значительно облегчает подготовку к занятиям. Однако часто требуется адаптация тестов к их использованию под конкретной тестирующей системой.
Важно, чтобы тесты не содержали ошибок. Первая реакция школьника на сообщение системы об ошибочности решения — «у меня все правильно, это у вас неправильные тесты». И здесь то, что на первых порах школьник получает тот тест, на котором его программа не работает, очень важно.
Он начинает смотреть этот тест и действительно находит ошибку в своей программе. Так формируется «авторитет» системы.
Если же часто будет оказываться, что решение школьника абсолютно правильное, и он был вынужден (из-за ошибок в тестах и в
проверяющей программе) искать в нем несуществующую ошибку и потратил на это очень много времени, то это, наоборот, работает на разрушение авторитета. Если же у системы не будет авторитета, убедить школьника искать ошибку и добиваться прохождения всех тестов будет очень-очень сложно.

Еще один важный момент — это то, что система тестов для задачи должна быть полной. Если в решении есть ошибка, то она обязательно
должна тестами находиться. Например, если задача формулируется: «дано до 100 чисел, найти минимальное из них», то обязательно должен быть тест именно на 100 чисел (потому что если школьник, например, несколько некорректно работает с массивом и обращается к (n+1)-му его элементу, то эта ошибка проявится лишь на таком тесте). Полнота системы тестов также работает на укрепление «авторитета» системы — получить от системы сообщение «задача решена» становится престижно.
И, сдавая свое решение на проверку, школьник в этой ситуации уверен, что его решение будет полноценно проверено (в противовес ситуации, когда школьник долго-долго возился с программой, подошел учитель, запустил ее на одном тесте и сказал: «Молодец, Вася»).
Еще один аспект автоматической проверки — полная беспристрастность. Все решения проверяются абсолютно одинаково. У системы не
бывает «любимчиков», которым прощается то, что не прощается другим, не бывает нелюбимых учеников.

Роль учителя

Может показаться, что при использовании системы автоматической проверки учитель вообще не нужен. Это не так.
Благодаря использованию системы автоматической проверки, во время урока у учителя освобождается время, и теперь он может больше времени посвятить тем ученикам, которым нужна его помощь.
Учитель нужен, чтобы помогать ученикам искать нетривиальные ошибки в программах, когда им не удается найти их самостоятельно.
Учитель нужен, чтобы давать подсказки, наводящие предложения ученикам, когда у них что-то не получается.
Учитель нужен, чтобы с учениками, решившими задачу, обсуждать, какие еще бывают способы ее решения, как решить задачу красивее.
Наконец, полезно время от времени после сдачи программы системе автоматической проверки заставлять школьников сдавать программу учителю. Во-первых, при автоматической проверке не отслеживается, чтобы код программы был написан красиво. Конечно, понятие красоты очень субъективное. Однако нужно требовать от школьников выделения логических блоков, циклов и т. д. Как уже говорилось, программа — это отражение мыслей школьника. Наводя «красоту» в программе, на самом деле мы наводим порядок в его мыслях. А пока в программе каша — в голове будет то же самое.
Кроме того, изучая код программы, можно увидеть и показать школьнику, что и как можно делать красивее и проще.

Чему учатся школьники, изучая программирование

Прежде всего, школьники учатся очень точно и очень формально излагать свои мысли. Во-вторых (и об этом тоже уже говорилось), они учатся работать самостоятельно, экспериментировать, искать пути решения возникшей проблемы.
Школьники учатся анализировать задачу, выделять, какие бывают ключевые моменты, где можно допустить ошибку
Школьники учатся тестировать свои программы, а значит, учатся относиться критически к продуктам своего труда. Рассмотрим ситуацию, когда школьник получил от системы ответ, что в программе есть ошибки, но не получил теста, на котором эта ошибка проявляется. Тогда ему прежде всего нужно подобрать тест, на котором его программа работает неправильно (а дальше с помощью отладчика найти, что же именно в ней неверно).
Возникает ситуация, когда школьник радуется, если ему удается найти тест, на котором его программа не работает. Согласитесь, что в педагогическом процессе такая ситуация бывает не часто.
Написание программ позволяет школьнику лучше понять как устроен и работает компьютер, какие задачи с его помощью можно решать.
Решая уже более сложные задачи, участвуя в олимпиадах, школьник знакомится с теорией алгоритмов. Учится применять стандартные
алгоритмы и адаптировать их для нестандартных задач. Учится строить математическую модель по словесному описанию условия задачи. Учится оценивать сложность алгоритма и скорость работы программы (часто до того, как начинает эту программу писать).
Участвуя в командных олимпиадах, школьники учатся работать в команде, распределять обязанности, совместно работать на общий результат.
Мне кажется, что большинство этих навыков будут полезны, даже если в дальнейшем жизнь школьника никак не будет связана с
программированием.

материалы по программированию