Электронное обучение основам программирования: проблематика и подходы

Дмитрий Григорьевич Хохлов,

доцент, к.т.н.

Кафедра автоматизированных систем обработки информации и управления

Казанский национальный исследовательский технический университет

им. А.Н. Туполева (КНИТУ-КАИ),

ул. К. Маркса, 10,  г. Казань, Россия, 420111, (843)2310028,
HohlovDG@mail.ru

Аннотация

Рассмотрена эволюция программирования как учебной дисциплины, приведшая к идеям создания системы электронного обучения основам программирования. Дан краткий обзор подходов к построению такой системы и примеры реализации ее фрагментов.

Evolution of programming as a learning discipline is described, leading to ideas of creating electronic learning system for basic programming. A brief review of approaches to such system development and examples of their implementation are considered.

Ключевые слова

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

programming,  principles and evolution of programming, electronic learning system, multi-agent approach

 

Введение

Проблемы автоматизации обучения программистов серьезно интересовали автора и его коллег с 1970-х годов, несмотря на ограниченные возможности компьютеров того времени [1].

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

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

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

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

Эволюция программирования как учебной дисциплины

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

 До 1970-х годов программирование, в основном, понималось и преподавалось как составление программ на машинном языке – кодирование алгоритмов в двоичной  системе счисления. Это устаревшее представление и неудачный термин «кодирование» встречаются и в наши дни.

Большинство учебников (например, [2]) начиналось примерами алгоритмов в словесной форме и в виде блок-схем, много страниц посвящалось системам счисления, затем приводились фрагменты программ на машинном языке реального или учебного компьютера и лишь в конце кратко рассматривались элементы языка программирования, чаще всего Algol-60.  Все это преподносилось, как правило, на бумаге без отладки программ на ЭВМ. Даже специализирующиеся в этой области студенты изучали программирование на последнем курсе и не имели опыта отладки программ.

Важной вехой стало появление на рубеже 1960-х и 1970-х годов идей структурного  программирования Э. Дейкстры и Ч. Хоора [3] и ориентированного на него новаторского учебника Н. Вирта [4], посвященного систематическому конструированию алгоритмов с использованием разработанного его автором удачного учебного языка Pascal, который до сих пор остается основным языком программирования для начинающих в большинстве средних и высших учебных заведений.  

В 1970-х - 1980-х годах появились, наконец, книги, посвященные важным, прежде всего, для системных программистов, методам нечисленного программирования: разработке и использованию сложных структур данных: стеки, очереди, строки, списки, графы, деревья, таблицы; методам трансляции и др. [5 - 7]. В эти же годы была издана долгожданная литература по технологии разработки и отладки многомодульных программ [8, 9].

Ранее эти методы составляли программистский фольклор и отсутствовали не только в учебниках, но и в доступной монографической литературе. Например, в дипломном проекте при разработке макропроцессора на языке ассемблера в 1969 – 1970 годах автор использовал древовидные таблицы, идею которых подсказал научный руководитель Ф.З. Рохлин, заимствовал приемы обработки символьной информации непосредственно из текста программы ассемблера, любезно предоставленного его разработчиком Ю.М. Баяковским, а алгоритмы трансляции был вынужден изобретать самостоятельно.

Учебник Н. Вирта [4] производил хорошее впечатление. Однако изложение программирования в таком стиле оказалось слишком сложным для первокурсников технического вуза, особенно идеи верификации программ. Кроме того, что этот учебник излишне для начинающих математизирован, в нем отсутствовали важные для практики элементы технологии разработки и отладки программ, а трансляторы языка Pascal в нашей стране в то время были недоступны.

Поэтому   до конца 1980-х годов приходилось преподавать технологию структурного программирования с использованием менее подходящего для обучения языка PL/1 и его реализаций для IBM/360, IBM/370 и совместимой с ними широко распространенной в нашей стране системы ЕС ЭВМ  [10, 11].

Методы нечисленного программирования и передовая технология структурного программирования с трудом проникали в учебники для вузов.

В 1985 – 1987 годах, когда в школах СССР вводили изучение информатики, а учителей для этого не было, автору в течение двух лет, параллельно с основной работой, пришлось читать лекции по этому предмету для сотен учителей информатики города Казани и преподавателей всех специальностей своего института, чтобы они не уступали в знаниях новым абитуриентам.

Учебник информатики для средней школы [12], по мнению автора, превосходил вузовские учебники того времени по научному и методическому уровню. Однако даже в нем были досадные   противоречия между новаторскими идеями ориентированной на структурное программирование первой части и преподносимым во второй части архаичным  стилем программирования на языке BASIC, который вполне можно было улучшить, на что приходилось обращать внимание слушателей.

В конце 1980-х годов появилась добротная отечественная литература, ориентирующаяся на язык Pascal и вполне приемлемая  для обучения студентов, но не рассматривающая технологию разработки и отладки программ, например, книга  [13], удачный практикум [14] и др.    

Большой интерес по содержанию и методике изложения вызвал новаторский учебник [15], но он  ориентирован на студентов-математиков МГУ и алгоритмический язык с русской лексикой, абстрактное  изложение не касалось технологии разработки и отладки программ, и требовалась существенная переработка для его использования в техническом вузе. Хорошим школьным учебником стал его адаптированный вариант [16].

В 1970 году автор читал отчет, в котором насчитали в СССР около 1000 системных программистов, а сейчас в мире свыше 7 миллионов  разработчиков и миллиарды пользователей программного обеспечения, и эта индустрия затрагивает интересы практически всех людей, независимо от области их деятельности.

Программирование, в широком смысле, как наука о программах, включает теорию, методы и средства разработки, производства и эксплуатации программного обеспечения компьютеров.

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

Вспоминается 1993 год, когда автор попал на экскурсию на завод по производству программного обеспечения и вместе с гостями – заведующими родственными кафедрами со всей России, с удивлением узнал, что программная продукция всех мировых фирм, продаваемая в красивых коробочках  на пространстве Европы и стран СНГ: от Исландии до Тихого океана, производится на двух маленьких заводах (порядка 10 работников в небольшом помещении), один из которых – в Шотландии, а другой, куда была организована экскурсия – в Казани.

Программирование – это синтезирующая многоплановая дисциплина, в которой можно выделить три основных компонента. С одной стороны, программирование образует ветвь прикладной математики и составляет часть общего математического образования. В то же время, это – инженерная наука (программная инженерия – software engineering) и важнейшая отрасль промышленности с оборотом в сотни миллиардов долларов [17, 18].

Программирование кардинально изменяется,  а преподавание отстает от этих изменений. В учебной литературе эти три компонента программирования рассматриваются, как правило, в отрыве друг от друга.

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

Прекрасные переводные книги по программированию на языках Pascal и С [19 − 21], по разработке и анализу алгоритмов [22  – 26]  и ряд других зачастую весьма объемны и также рассматривают лишь отдельные аспекты программирования.

Разработка программного обеспечения, как правило, излагается в отрыве от его реализации, без подробных комплексных примеров  [17, 21]. Во многом это объясняется методической сложностью: большой пример трудно разработать и подробно рассмотреть, а маленькие примеры не отражают возникающие здесь трудности и применяемые методы.

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

Попыткой рассмотреть программирование как целостную науку была интересная и широкая по охвату материала книга [27], воплощающая концепцию многоязыкового или, как назвал ее академик А.П. Ершов,  надъязыкового обучения, и нацеленная на преодоление засилья американских традиций в данной области. Однако набор базовых языков учебника: ФОРТРАН, Алгол W и ПЛ/1  был не актуален для нашей страны, объем материала не вписывался в учебный план, и эту книгу можно было использовать только как дополнительное пособие, а не базовый учебник.

Более глубокие по содержанию книги, например [28, 29], не рассчитаны на начинающих и не рассматривают технологию разработки, а поэтому тоже не могут служить базовым курсом программирования.

Поскольку в нашей стране не оказалось общепринятого учебника  программирования, каждый вуз выходил из положения по-своему.  Для преодоления этой ситуации автору вместе с коллегами по кафедре пришлось написать более 30 пособий. Одним из итогов этой работы стал учебник [30, 31], обобщающий многолетний опыт кафедры  в преподавании базового курса программирования,  и учебные пособия по смежным дисциплинам [32, 33]. 

В учебнике    [30, 31]  сделана попытка, начиная с первого курса, сбалансировано и с единых позиций изложить основные концепции и методы структурного и модульного программирования, обращая внимание  на   технологические аспекты и потенциальные приложения, и не слишком привязываясь к особенностям конкретного языка программирования.

В качестве базового языка выбрано  общее подмножество языков С и С++, наиболее подходящее, по мнению автора, для  будущих профессионалов. Алгоритмы, как правило, представляются на псевдокоде – неформальном расширении языка С.

В последующие годы базовые знания  подкрепляются курсами объектно-ориентированного программирования, технологии программирования и знакомством с  языком ассемблера в дисциплинах по операционным системам и трансляторам [32, 33].  

Во всех пособиях автор старался  уделять особое внимание наиболее общим закономерностям программирования и информатики.

 

Законы и принципы программирования

 

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

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

Чаще всего упоминают важные результаты математической логики и теории алгоритмов об эквивалентности разных формализаций понятия алгоритма и наличии алгоритмически неразрешимых проблем [34]. Ссылаются также на теорию вычислительной сложности, связанную с пространственно-временной закономерностью, в частности, на знаменитую проблему NP-полных задач [25].

М Холстед предложил изучать программирование методами естественных наук: выдвигать гипотезы и проверять их справедливость на опыте [35]. В основу его метрической теории программ в качестве научной гипотезы положено уравнение длины программы:

LT = n1 ∙ log2 n1 + n2 ∙ log2 n2                                              

где: LT – теоретическая длина программы (количество лексем – слов),

n1 – число разных операндов программы (длина словаря операндов),

n2 – число разных операторов программы, включая символы-разделители, имена процедур и знаки операций (длина словаря операторов).

Например, в программе

if (a > b) x = a; else x = b;

три разных операнда:  a, b, x  (n1 = 3) и пять разных операторов: if-else, (), >, =, ; (n2 = 5). Тогда, из уравнения длины программы, ее теоретическая длина   

LT = 3 ∙ log2 (3) + 5 ∙ log2 (5) = 16,36 лексем.

Нетрудно видеть, что фактическая длина программы составляет LФ  = 15 лексем, что достаточно близко к ее теоретической длине  (на 8,3 % меньше).

Однако к этой программе можно приписать последовательность из произвольного числа ничего не меняющих операторов, например, вида: x=x; x=x; и т. д. Подобным образом можно беспредельно увеличивать фактическую длину программы, не меняя смысла программы, числа разных операндов и операторов в ней и ее теоретической длины. Следовательно, уравнение длины не корректно?

Это не так, данную  ситуацию следует трактовать иначе, − говорит М. Холстед. Уравнение длины рассчитано на разумно написанную программу, а разве приписывание таких заведомо излишних операторов разумно! 

Значительное превышение фактической длины над теоретической длиной программы, например, более чем на 10 – 15 %, свидетельствует о наличии разных типов «несовершенств» в программе и может служить критерием ее качества.

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

Гораздо реже приводятся отдельные примеры краевого (пограничного) эффекта, связывания, антропоморфизма,  не называя и не формулируя эти закономерности в общем виде. Некоторые из этих явлений относятся не только к программированию, но и являются проявлениями более общих  законов.

Пространственно-временная закономерность связывает две важнейшие характеристики программы: время работы и объем памяти компьютера, необходимые для ее исполнения, т. е.  эффективность программы по времени и памяти.

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

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

Краевой (пограничный) эффект – это один из общих законов природы и общества – появление аномальных, чаще всего неприятных явлений на краю, на границах, на стыке разных областей: «на краю – все не так, как внутри, и обычно хуже» или, как образно поется в известной песне, «На границе тучи ходят хмуро».

Примеры – на каждом шагу: вулканы, землетрясения и цунами на границах плит земной коры; ураганы и тайфуны на границе моря и суши; искривление поверхности жидкости у стенок сосуда; проверка паспортов и виз, контрабанда, вооруженные столкновения и другие конфликты на границах государств; ухудшение качества дорог на границах административных районов или грязь на границе между дворами (там уже не наша территория) и т.п.

В программировании необходимо сознательно учитывать краевой эффект при разработке программ и тестов для их отладки, при поиске ошибок в программе  [30, 31].  

Из-за краевого эффекта ошибки в программах чаще всего проявляются вблизи граничных значений величин.

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

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

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

Связывание (binding) – это очень многозначный термин. Будем под ним понимать принятие решения – выбор одного из возможных вариантов, который увеличивает определенность ситуации, что дает дополнительную информацию для более эффективных действий, но уменьшает гибкость − степень свободы принимающего решение, возможности подстраиваться под изменения ситуации.

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

Важно осознанно и правильно выбирать время связывания – момент принятия решения. Откладывание связывания сохраняет гибкость − свободу принятия решения, в ущерб возможной эффективности.

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

Термин “связывание”, возможно, пришел из математики, где различают свободные и связанные переменные.

В программировании под связыванием понимают решение, влияющее на  характеристики программ [36]. Связывание до начала исполнения программы называется статическим, во время исполнения – динамическим.

Практически, почти все, что происходит в программировании, является разновидностью связывания: действия людей, программ и компьютеров во время разработки и реализации языка программирования, разработки программы и выбора языка для ее реализации, на всех этапах трансляции и исполнения программы. 

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

Во время исполнения программы происходят  динамические связывания: при входе в подпрограмму или блок выделяется, а при выходе освобождается область памяти для параметров и локальных переменных; в произвольные  моменты во время работы программы вызываются и загружаются динамически подключаемые подпрограммы; программы распределения памяти ищут свободные места для динамических переменных и освобождают их; а также выполняются присваивания − связывания переменных с их значениями. Непосредственно при выполнении машинной команды заданный в ней виртуальный адрес аппаратно-программным способом преобразуется в реальный адрес оперативной памяти.

Раннее связывание обычно увеличивает эффективность программы по времени, за которую приходится платить ухудшением эффективности по памяти  и  гибкости в использовании программы.

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

Так, главной особенностью языка Java, объясняющей его быстро растущую популярность, являются максимально поздние динамические связывания, обеспечившие гибкость и мобильность Java-программ − возможность их исполнения в сети Интернет на компьютерах разного типа в среде различных операционных систем. Однако это замедляет выполнение программ. 

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

Автор часто повторяет студентам: «в программах – все, как у людей!». Это видно даже в терминологии  – мы говорим: «программа» (а не компьютер!) «вводит, вычисляет, печатает, ищет, спрашивает, ведет  диалог, обладает определенным поведением» и т.п. 

Такие антропоморфные представления – не просто образные сравнения. Они отражают объективную закономерность и  помогают понимать,  объяснять, использовать и разрабатывать принципы построения и функционирования программ [30 - 32].

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

Программы, подобно живым существам,  прерывают работу, чтобы «реагировать на важные для них события».

Каждая подпрограмма, на машинном уровне, сначала подобно человеку готовит себе «рабочее место» − выполняет «пролог» − подготовительные действия  для формирования кадра стека с параметрами и локальными переменными; затем делает свою основную «работу», а в завершение − в  «эпилоге» – «убирает за собой» кадр стека.

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

С усложнением программ они все больше напоминают не отдельного человека, а сообщество людей. По аналогии с антропоморфизмом, для этого можно  было бы ввести термин социоморфизм (от лат. soci(etas) – общество и греч. morphē − форма) − подобие, аналогия между явлениями в программной системе и коллективе людей, человеческом обществе (или сообществе животных).  

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

Подпрограмма вырабатывает код завершения, «докладывая», таким образом, «начальнику» − запустившей её программе, удалось ли ей выполнить «порученную работу». 

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

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

 

Электронное обучение основам программирования

Рассмотрим сначала требования к электронной системе обучения программированию  (ЭСОП) и решаемые в ней задачи, а затем подходы к  их решению. Особое внимание уделяется специфике обучения данному предмету.

 

Требования к ЭСОП

 

Согласно принципу антропоморфизма, абстрагируясь от неизбежных и возможных ограничений, идеальным конечным результатом (ИКР) можно считать реализацию в ЭСОП поведения, близкого к действиям преподавателя-репетитора при индивидуальном обучении или к функционированию образовательного комплекса с максимальным оснащением, уровнем разделения труда и комфортом для обучаемых и сотрудников, подобного Центру подготовки космонавтов с его тренажерами, преподавателями, инструкторами, тренерами, психологами, массажистами, медиками и т.п.

Такой ИКР полезен как определенный ориентир в разработке ЭСОП, хотя его и невозможно в полном объеме реализовать в ближайшем будущем.

Подобно преподавателю, ЭСОП в своих базах знаний и данных (БЗ и БД) должна обладать информацией о предметной области, педагогических принципах и методике обучения, индивидуальных психологических и других особенностях каждого обучаемого,  цели и истории его обучения и т.п.

ЭСОП должна «уметь» сообщать (излагать, предъявлять) необходимые знания обучаемому и проверять уровень их усвоения, адаптируясь под индивидуальные особенности каждого обучаемого. Необходимы также определенные элементы накопления и анализа опыта своей работы − самообучения ЭСОП.

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

Как и преподаватель, ЭСОП должна не только иметь заранее подготовленный набор таких задач, но и изменять их параметры и сложность или формулировать (генерировать) индивидуальные задачи для определенного обучаемого в конкретной ситуации.

Необходимо также определенное «умение» ЭСОП решать сформулированные ею  задачи (синтезировать программы,  тесты и чекеры для них) для проверки и оценки программ обучаемых и  помощи ему в их разработке.

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

В содержании и  представлении  учебного материала по программированию необходимо использовать:

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

§  различные критерии качества программ с учетом  противоречий между ними – на основе компромиссов;

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

Содержание обучения включает  знания и виды деятельности, основанные на этих знаниях.

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

Предметом обучения является также выработка  умения и навыков  в следующих видах деятельности:

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

§  математический анализ задачи, выбор и обоснование математического метода, структуры данных и алгоритма решения типовых задач;

§  решение типовых задач на составление программ: линейные алгоритмы, алгоритмы с ветвлением, однопроходные алгоритмы (подсчет количеств, сумм, произведений, максимума / минимума, поиск и т.п.); обработка одно- и двумерных массивов, использование подпрограмм, рекурсия, сортировка, алгоритмы на строках, алгоритмы на графах и деревьях и др.;

§  решение нестандартных (нетиповых) задач, для чего полезны олимпиадные задачи по программированию, например,  из [31, 32],  других пособий и специальных сайтов.

Перечисленные области содержат как артикулируемые (явные) компоненты знаний, так и неартикулируемые (неявные) знания, касающиеся использования большинства приемов и методов разработки структуры данных и алгоритмов, а также проектирования и отладки программ, и вырабатываемые упражнениями и решением задач [37].

ЭСОП должна соответствовать дидактическим требованиям и общим требованиям к электронным образовательным ресурсам − ЭОР [37, 38], в том числе обеспечивать:

§  поддержку самостоятельной учебной работы обучаемых на всех этапах их познавательной деятельности: первоначальное знакомство с учебным материалом, контроль его усвоения,    решение типовых и нетиповых практических задач;

§  максимальное использование средств мультимедиа: текста, графики, анимации и др., особенно  на этапах начального знакомства с учебным материалом;

§  индивидуализацию – учет и использование особенностей каждого обучаемого [39];

§  коммуникативность  оперативное взаимодействие обучаемых с преподавателем или между собой при коллективной работе;

§  непрерывное управление  учебным процессом для обеспечения гармонии взаимодействия его составляющих в условиях постоянного изменения характеристик обучаемых, обучающей системы и содержания обучения [39, 40];

§  интерактивное взаимодействие системы и ученика с обоюдной инициативой и использованием (ограниченного) естественного языка [41];

§  открытость – возможности и инструментарий для внесения изменений в ЭСОП, в том числе преподавателями-пользователями системы;

§  возможность повторного использования компонентов ЭСОП в других системах.

Концепция открытости и повторного использования компонентов ЭОР поддерживается такими международными стандартами электронного обучения, как  SCORM и IMS, а также в работах Национального фонда подготовки кадров при Правительстве России  [37].

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

Подобным требованиям в настоящее время в максимальной степени удовлетворяет многоагентная архитектура  (agentware). Многоагентные системы используют для решения проблем, которые сложно или невозможно решить с помощью монолитной системы или одного агента [42, 43].

ЭСОП строится  по модульному принципу в виде многоагентной системы  – совокупности взаимодействующих рациональных  (интеллектных или интеллектуальных)  агентов [44 - 49].

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

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

При сложном поведении, когда реакция  управляющего агента зависит не только от входного воздействия, но и от истории работы, его удобно реализовать по технологии автоматного программирования [50].

В основу реализации ЭСОП положен эволюционный подход: быстрое прототипирование и цикл развития – разработка, опытная эксплуатация и доработка. В настоящее время реализованы прототипы отдельных компонентов ЭСОП и разрабатываются принципы их интеграции.

 

Компоненты системы электронного обучения

 

Преподаватели, аспиранты и студенты кафедры разработали различные фрагменты и подсистемы электронного обучения базовому курсу программирования. В частности, имеется гипертекстовый вариант учебника [30, 31].

 

Контроль уровня подготовки

 

В курсе программирования используются разнообразные тренировочные и контрольные упражнения и задачи.

Проверка знаний основных понятий, терминов и закономерностей, правил синтаксиса и семантики языка программирования, как правило, реализуется   традиционным тестированием, рассчитанным на статические, т. е. заранее заготовленные задания.

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

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

Для этого необходимо развивать алгоритмическое воображение − способность программиста по записи алгоритма отчетливо представлять процесс его исполнения, по аналогии с необходимым инженеру-механику пространственным воображением − способностью представлять форму детали по ее чертежу в разных проекциях.

С этой целью для сложных алгоритмов разрабатывают визуализаторы − анимационные программы, в наглядной форме представляющие динамику их исполнения [50, 51].

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

Составление трассировочных таблиц систематически используется  автором при изложении учебного материала, а также  в тренировочных и контрольных заданиях на экзаменах и зачетах по программированию [30 - 33].

Для обучения составлению трассировочных таблиц, в том числе по программе самого обучаемого, и проверки этого умения разрабатывается специальный интерпретатор упрощенной учебной версии языка C на основе реализованного автором учебного транслятора C0 [31, 32].

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

 

Оценка качества программ ученика

 

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

Поэтому важнейшей функцией ЭСОП является оценка качества программ, написанных учениками [46, 49]. Такая оценка, особенно проверка корректности   работы программ, является наиболее трудоемкой частью работы преподавателя при обучении программированию, которой не всегда удается уделить должное внимание.

Большое влияние на преподавание и его автоматизацию оказали олимпиады и соревнования по программированию, в том числе  опыт автора и его коллег в их проведении и подготовке к ним студентов и школьников с 1995 года.

C 1996 года Россия начала регулярно участвовать в студенческом  командном чемпионате мира среди команд высших учебных заведений – International Collegiate Programming Contest, проводимом ACMAssociation for Computing Maсhinery (ACM ICPC) [52, 53].

На этих соревнованиях с 1995 года используются системы автоматического тестирования программ участников, появление которых послужило дополнительным стимулом автоматизации обучения программированию [54 - 58].

Этот опыт весьма полезен и для преподавания программирования, где он еще пока очень мало используется.

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

Поэтому для адаптации и эффективного использования олимпиадного опыта в учебном процессе требуются серьезные методические и научно-технические исследования и разработки.

Тестирование программы − это проверка ее работоспособности на наборе специально подобранных тестов. Тестирование позволяет также оценить эффективность программы по времени и памяти.

Для автоматического тестирования необходима четкая формальная постановка предлагаемых задач с указанием допустимых границ всех величин, формата входных и выходных данных, ограничений на время исполнения и объем памяти программы.

Подобный подход дисциплинирует обучаемых,  прививает им профессиональные навыки и с 2007 года используется автором и его коллегами на лабораторных занятиях, зачетах и экзаменах по программированию [46, 49, 59 - 62].

Существенным аспектом такого подхода является систематическая  разработка тестов для проверки корректности программ  [10, 30, 31].  

Далее в упрощенном виде на языке C приведены примеры некоторых программ тестирующей системы ЭСОП.

Для каждой задачи разрабатывается набор тестов, включающих пример входных данных и соответствующий результат решения задачи, а также чекер (от англ. checker - контролёр) - программа проверки результатов выполнения программы обучаемого.  На олимпиадах по программированию эта сложная, кропотливая и трудоемкая подготовительная работа выполняется вручную.

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

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

Чекер получает имена входного и выходного файлов программы учащегося и  ожидаемый эталонный ответ для этого теста и должен проверить наличие выходного файла и правильность находящегося в нем  результата.

В качестве простого примера рассмотрим. задачу составления программы для определения максимального значения  данной последовательности целых чисел.

Входные данные могут, например, иметь вид:  3,  1,  7,  5,   7,  4,  6, 7. Ожидаемый результат этого теста – число 7, присутствующее три раза. Чекер для такой задачи очень прост, поскольку результат исполнения проверяемой программы должен равняться ожидаемому значению. Этот чекер можно использовать для любой задачи, результатом которой   является однозначно определяемое целое число. 

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

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

В данной задаче при разработке чекера трудности возникают, когда требуется найти не значение максимального числа, а, например,  его номер в заданной последовательности.

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

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

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

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

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

Типовой чекер, пригодный для всех таких задач, ответом в которых является множество целых чисел из заданного диапазона от   L до R, имеет вид:

 

#define M_N_MAX 100000        // Максимальная мощность множества

#define MNMAX (M_N_MAX+7)/8  

#include <stdio.h>

FILE *fout;

 

//Чекер для множества n целых чисел y[n], (n = y[0]<=M_N_MAX)

 

int ChSN(int L, int R, char y[])  // y – эталонное множество

{  long n, i, r, ns;

   char s[MNMAX];    //s - множество целых чисел r от L до R

   ns = (R-L+1+7)/8;          // длина s в байтах

   n = y[0];                  // Количество чисел множества

   fscanf(fout,"%ld",&r);     // Ввод количества чисел ответа

   if (r != n)                // неверное количество чисел

      return 'A';             // _wa - wrong answer

   for(i=0; i<=ns; ++i) s[i]=0; // пустое множество-эталон

   for(i=1; i<=n; ++i) {

     r = y[i];                 // включить y[i] в множество s

     s[r/8] |= 1<<(r%8);       //

   }

   for(i=1; i<=n; ++i) {

     if(fscanf(fout,"%ld",&r)<1)//ввод ответа r, конец файла?

        return 'P';               // _pe – presentation error

     if(r>=L && r<=R) r -= L;    // число r внутри [L, R]

     else   return 'A';          // _wa - неверный ответ

     if(((1<<(r%8))& s[r/8])==0) // числа r нет в множестве s

        return 'A';               // _wa - неверный ответ

     s[r/8] &= ~(1<<(r%8));      // удалить r из множества s

   }

   return '+';                   // _ok – верный ответ

}

 

В этом чекере множество целых чисел представлено характеристическим вектором. Каждый бит вектора соответствует возможному значению числа и равен 1, если это число принадлежит множеству, и 0 − в противном случае. Включение числа во множество выполняется поразрядной операцией ИЛИ, а исключение  − поразрядной операцией И. Для этого используется единичный бит, сдвигаемый влево на число разрядов, равное данному числу.

Реализация системы тестирования программ

Рассмотрим в упрощенном виде пример комплекта простой типовой задачи с оценкой ее решения на тему реализации программ обработки графов [30, 31].

Задание сформулировано следующим образом: «Дан граф в виде числа вершин и матрицы смежности. Составить подпрограмму получения множества ребер графа. Привести пример вызова подпрограммы. Число вершин графа не превышает 5.».

Решением этой задачи является последовательность индексов строки и столбца единичных элементов заданной квадратной  матрицы из нулей и единиц. Предполагается, что вершины графа и соответствующие им строки и столбцы матрицы пронумерованы от 0 до n − 1, где   n - число вершин графа.

Поскольку для графа эта матрица симметрична относительно главной диагонали, необходимо рассматривать только половину матрицы − элементы матрицы, расположенные на главной диагонали (идущей из левого верхнего угла в правый нижний угол) и выше (либо - на главной диагонали и ниже ее). Порядок элементов не имеет значения.

Например, матрица размера 4 × 4 задана следующим образом.

Входные данные включают  n = 4 и матрицу смежности:

   4

   0 1 1 0   

   1 0 1 1   

   1 1 1 1   

   0 1 1 0   

 

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

0 1  0 2  1 2  1 3  2 2  2 3.

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

1 0  2 0  2 1  2 2  3 1  3 2.

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

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

Ответ (множество ребер графа) здесь определяется однозначно, но в виде последовательности элементов может представляться многими способами.  

Решением задачи является подпрограмма rebra и ее вызов rebra(n, m); (они выделены в программе полужирным шрифтом), которые студент должен вставить в предоставленный ему шаблон:  

 

#define NMAX 5          // Число вершин максимальное     

FILE *fin, *fout;       // Входной и выходной файлы

#include "GRAF.C" // Функции ввода и вывода матрицы смежности

 

// Вывод перечня ребер графа из матрицы смежности

void rebra(int n, int msm[NMAX][NMAX])

{ int     i, j;            // Индекс строки и столбца

 

  // Просмотр матрицы смежности по главной диагонали и выше     

  for (i=0; i<n; i++)

    for (j=i; j<n; j++)

      if (msm[i][j])  fprintf (fout,"%d %d  ", i, j);

}

int main()

{ 

  int     n;                 // Число вершин                    

  int     m[NMAX][NMAX];     // Матрица смежности

 

  if ((fin=fopen("input.txt","r"))==NULL)

  {  puts ("File input.txt not found");

     return 1;       // Ошибка: нет входного файла

  }

  if ((fout=fopen("output.txt","w"))==NULL)

  {  puts ("File output.txt is not opened");

     return 2;       // Ошибка: нет выходного файла

  }

   ReadMsm(&n, m);   // Ввод матрицы смежности

//  PrintMsm(n, m);  // Вывод матрицы смежности

   rebra(n, m);      // Получение и вывод решения задачи

   fclose(fin); fclose(fout);

   return 0;         // Успешное завершение программы

}

 

В этом шаблоне при необходимости можно использовать подпрограммы ввода и вывода графа, например, в виде матрицы смежности ReadMsm(&n, m) и PrintMsm(n, m), предоставленные студенту в файле GRAF.C. В данной программе вывод не использован – «закомментирован».

Одним из тестов может служить приведенный выше пример. Входные данные теста включают  число вершин n и матрицу смежности:

4

0 1 1 0 

1 0 1 1 

1 1 1 1 

0 1 1 0 

 Ожидаемый результат – любой вариант последовательности ребер графа, например:

0 1  0 2  1 2  1 3  2 2  2 3

 

Тестирующая система ЭСОП  содержит паспорта учебных задач в виде массива структур prblm[].

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

 

//         Данные о тестируемых задачах

#define KT  10       // Максимальное число тестов задачи

 

struct PROBLEM {             // Форма паспорта задачи

  char * prblkod;    // код (номер) задачи

  FCHEKER cheker;    // указатель на функцию-чекер

  int kolt;          // число тестов

  int bon_bal[KT+1]; // бонус и баллы за тесты

  char* prblh;       // заголовок (смысл) задачи

  char* prblt;       // текст условия задачи

  char *t[KT][2];// Входные и выходные данные тестов (строки)

}

 prblm[] =

{           // Паспорта задач  

 

/* ------ ШАБЛОН задачи - поля struct PROBLEM в {} через , :

// код_зад имя-чекера, кол-тестов, {бонус, балл1,...,баллK},

// prblkod  cheker      kolt        bon_bal

 

  {  "00.4", ChNN,       4,        {5,  2,2,2,2},  // 13=5+8 

   "ЗАГОЛОВОК задачи" ,          // prblh

   "УСЛОВИЕ задачи Строка 1\n"   // prblt

   " ... любое число строк \n"   //

   "        ...    Строка N." , //

   // Тесты задачи, до KT тестов, ТЕСТ = 2 строки:

   "Вход 1",  "Выход 1",... , "Вход K", "Выход K"},  //char*t[][2]

  },

   ------------------------------------------------------ */ 

Для рассматриваемой задачи паспорт может выглядеть следующим образом:

 

// Пример: Problem 00.4 (4.7): 4 теста, до 8+8 баллов

  { "00.4", ChSM_N, 4, {8,  2,2,2,2  }, //16  8+8 

   "Ребра графа из матрицы смежности",     //Заголовок

   "Дан граф в виде числа вершин и матрицы смежности.\n" // prblt

   "Составить подпрограмму получения последовательности\n"

   "ребер графа. Привести пример вызова подпрограммы. \n"

   "Число вершин графа не превышает 5.",

 // тесты,  вход:      выход:

 // 1) n  матрица       Ребра

      "5\n0 1 0 1 0\n"

         "1 0 0 0 1\n"

         "0 0 0 1 0\n"

         "1 0 1 0 1\n"

         "0 1 0 1 0\n", "0 3  0 1  1 4  2 3  3 4",

 // 2) n  матрица       Ребра              

      "4\n0 0 1 1\n"

         "0 1 0 1\n"

         "1 0 1 0\n"

         "1 1 0 0\n",  "2 0  0 3  3 1  1 1  2 2",

 // 3) n  матрица       Ребра              

      "4\n0 0 0 0\n"

         "0 0 0 0\n"

         "0 0 0 0\n"

         "0 0 0 0\n",  "",

 // 4) n  матрица       Ребра              

      "3\n1 1 1\n"

         "1 1 1\n"

         "1 1 1\n",  "0 1 0 0 2 0 2 2 1 1 2 1",

  },

 

Чекер для проверки равенства двух множеств пар целых чисел имеет вид:

 

#define M_N_MAX 100 //Максимальное число значений элемента множества

#define MNMAX (M_N_MAX*M_N_MAX+7)/8 // Число байтов харак-го вектора

 

// Чекер для множества m*n пар целых чисел (i,j) m,n <= M_N_MAX

// Li<=i<=Ri, Lj<=j<=Rj, m=Ri-Li+1, n=Rj-Lj+1, y – эталонное множ-во

// order = 0 - порядок в паре не важен, != 0 - важен

 

int ChSM_N(int Li, int Ri, int Lj, int Rj, char y[], int order)

{  long n, m, k, i, r, r1, r0, ns;

   char s[MNMAX];   // s - множество целых r от 0 до M_N_MAX-1

   m = Ri-Li+1; n = Rj-Lj+1;      // длины диапазонов двух чисел

   ns = (m*n+7)/8;                // длина s в байтах

   k = y[0];                      // Количество пар чисел множества

   fscanf(fout,"%ld",&r);         // Ввод количества пар ответа

   if (r != k)                    // неверное количество пар

      return 'A';                 // _wa - wrong answer

   for(i=0; i<=ns; ++i) s[i]=0;   // пустое множество-эталон

   for(i=1; i<=2*n; i+=2) {

     // включить (y[i],y[i+1]) в множество s

     r=n*(y[i]-Li)+y[i+1]-Lj;     // упак-ть в r пару: y[i],y[i+1]

     if (! order)                 // порядок не важен

       if (y[i] > y[i+1])

         r=n*(y[i+1]-Lj)+y[i]-Li; // упорядочим: y[i] <= y[i+1]

     s[r/8] |= 1<<(r%8);          // включить r в множество s

   }

   for(i=1; i<=k; ++i) {

     if(fscanf(fout,"%ld",&r)< 1) // ввод ответа r, конец файла?

       return 'P';                // _pe – presentation error

     if(r>=Li && r<=Ri) r -= Li;  // число r внутри [Li, Ri]

     else   return 'A';           // _wa - неверный ответ

     if(fscanf(fout,"%ld",&r1)<1) // ввод ответа r1, конец файла?

       return 'P';                // _pe – неверный вид ответа

     if(r1>=Lj && r1<=Rj) r1-=Lj; // число r1 внутри [Lj, Rj]

     else   return 'A';           // _wa - неверный ответ

     if (! order)                 // порядок не важен

       if (r > r1) {

           r0=r; r=r1; r1=r0;     // упорядочим: r <= r1

       }

     r = n * r + r1;              // упаковать в r пару (r,r1)

     if(((1<<(r%8)) & s[r/8])==0) // пары r нет в множестве s

       return 'A';                // _wa - неверный ответ

     s[r/8] &= ~(1<<(r%8));       // удалить r из множества s

   }

   return '+';                    // _ok – верный ответ

}

 

Рассмотрим пример несложной, но нетиповой задачи определения дня недели заданной даты по новому стилю, например, 8.3.2012  выпадает на четверг [30].

  Паспорт этой задачи содержит ее полное условие и может выглядеть следующим образом:

 

// Пример: Problem 00.99: 7 тестов, до 18=4+14 баллов

  { "00.99", ChS, 7,{4,2,2,2,2,2,2,2}, //18 4+14 

   "День недели",     //Заголовок и условие задачи  

   "Даны три числа D, M, G,обозначающие дату: день,\n"

   "месяц и год (0<D<32, 0<M<13, 1<=G<=3000). Определить \n"

   "день недели этой даты  (по новому стилю),  учитывая, \n"

   "что 1 января 1 года н. э. был понедельник. Указание:  \n"

   "високосными считаются те годы, которые делятся на 400\n"

   "и те, которые делятся на 4, но не делятся на 100.",

 //  Тесты - вход:      выход:           номер

 //     Дата        День недели

  "8     1   1",     "понедельник",   // 1

  "29    2   2000",  "вторник",       // 2

  "31    1   2001",  "среда",         // 3

  "1     3   2012",  "четверг",       // 4

  "24    2   2012",  "пятница",       // 5

  "6     3   2900",  "суббота",       // 6

  "28   12   3000",  "воскресенье",   // 7

},

 

Программа решения задачи на языке Pascal  может иметь вид:

 

{     DN. День недели  Д.Г. Хохлов  11.12.00               }

const  dn: array [0..6] of string=('понедельник', 'вторник',

   'среда', 'четверг', 'пятница', 'суббота', 'воскресенье');

   dm: array [1..12] of longint=(0,31,59,90,120,151,181,212,

    243,273,304,334);      { dm[m]=кол. дней от 1.1 до 1.m }

var d,m,g,nd: longint;

begin

   assign (input, 'input.txt');  reset(input);

   assign (output,'output.txt'); rewrite(output);

   read(d,m,g);

   nd := g-1 + g div 400 + g div 4 - g div 100 + dm[m]+d-1;

   if (m<3) and ((g mod 400 = 0)

     or (g mod 4 = 0) and (g mod 100 > 0))

   then nd := nd - 1;   { январь/февраль високосного года }

   writeln (dn[nd mod 7]);

   close(output); close(input);

end.

 

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

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

Наглядность, понятность и простота изменений программы зависят от стиля программирования, который неформально оценивается визуальной привлекательностью программы и удобством ее изменения. Стиль влияет на корректность и надежность программы, трудоемкость ее разработки, отладки, сопровождения, развития и использования в других проектах [9, 30, 31, 63].

В ЭСОП  стиль программирования оценивается по таким параметрам, вычисляемым по тексту программы, как её длина, количество комментариев и пустых символов, длина и содержательность имен и т. п. [46, 49, 62].  

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

Кроме того, для оценки программ используется метрическая теория программ Холстеда. По отличию фактической длины от теоретической длины программы определяется «несовершенство» программы, «интеллектуальное содержание», уровень программы и другие объективные показатели ее качества [35, 46].

Для программной реализации метрической оценки и оценки стиля проводится лексический  и частичный синтаксический анализ текста программы, зависящий от языка программирования.

Подсистема комплексной многокритериальной оценки ЭСОП  тестирует работоспособность и эффективность программы обучаемого, оценивает стиль программирования, метрические характеристики программы, а также ее сложность с учетом количества ветвлений, циклов и их вложенности с помощью цикломатической меры Мак-Кейба. Эта методика реализована для языков C и Pascal [46].

После проведения оценки подсистема выдает рекомендации для улучшения программы.

В качестве примера рассмотрим  неудачно оформленную учебную программу с лишними скобками и присваиваниями, бессодержательными именами переменных m1, v1, излишней переменной i2, явной (неименованной) константой 100, затрудняющей модификацию программы:

 

#include <stdio.h>

int main() {

      int m1[100];

          int v1;

                       scanf("%d",&v1);

               for((int i1=0);(i1<100);((i1))++)

                scanf("%d",&m1[i1]);

                      for(int i2=0;i2<100;i2++)

                  {if((m[i2])>(v))

              printf("%d ",&m1[i2]);}

if(true)

            if(true)

      if(true)

            while(v1<200)

               v1=v1=v1=v1=v1=v1=v1++;

         return 0;

}

 

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

Выполнив  рекомендации, получим примерно такую программу:

#include <stdio.h>

 

#define SIZE      100         //размер массива

 

/*главная функция*/

int main() {

   int num[SIZE];             //массив чисел

   int max;                   //максимальное значение

 

   scanf("%d",&max);

 

   for(int i=0;i<SIZE;i++)    //ввод чисел

      scanf("%d",&num[i]);

 

   for(int i=0;i<SIZE;i++) {  //вывод чисел

      if(num[i]>max)

         printf("%d ",&num[i]);

   }

 

   return 0;

}

 

Программа стала более наглядной и понятной и получает хорошую оценку 95,50 баллов.

 

Генерация задач и программ

 

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

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

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

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

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

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

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

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

Для каждого типа задач имеется шаблон (фрейм) условия задачи и шаблон эталонной программы ее решения.

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

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

Программы генерации задач и решений  написаны на языке С. Рассматриваются возможности использования для этой цели других языков и средств, в частности, макропроцессоров, например, ML/1 [64, 65]. Исследуются возможные методы генерации нестандартных чекеров для задач с неоднозначным  результатом. 

 В качестве примера рассмотрим  задачу на тему  обработки двумерного массива.

 

 Пример обработки двумерного массива.

 

По заданному набору параметров из имеющегося шаблона сгенерирована задача с условным номером 12345 и решающая ее эталонная C-программа с комментариями, включающими условие задачи:

 

// Z12345.cpp           Система ЭСОП  TUTOR v 1.0                

// Решение задачи 12345: Составить программу решения следующей задачи.

// Ввести по строкам вещественную прямоугольную матрицу. Число строк

// и столбцов не более 20. Найти максимум  средних арифметических

// значений положительных элементов каждого столбца. Вывести максимум.

// При отсутствии в матрице  положительных элементов вывести No numbers.

 

#include <stdio.h>

#define MMAX 20

typedef double real; // Вещественный тип

real x[MMAX][MMAX];  // исходная матрица

int  m, n,           // кол строк и столбцов

     i, j;           // индекс строки и столбца

real max;            // искомый максимум

int  f_max;       // флаг 1 - нашли max, 0 - нет нужных чисел

real sum,sr_arif; //сумма и сред. арифм. полож. чисел столбца

int  npoz;        // количество положительных чисел столбца

 

int main()

{               

  // Ввод матрицы x по строкам;

  scanf("%d %d", &m, &n);

  for(i=0; i<m; i++)           // номер строки

    for(j=0; j<n; j++)         // номер столбца

       scanf("%lf", &x[i][j]);

  // Просмотр матрицы x по столбцам для получения max

  f_max=0;

  for(j=0; j<n; j++) {         // номер столбца

    // sum = сумма положительных элементов столбца j,

    // npoz = количество положительных элементов столбца j

    npoz=0; sum=0;

    for(i=0; i<m; i++) {       // номер строки

      if(x[i][j]>0) {

        sum+=x[i][j];

        npoz++;

      }

    }

    if(npoz) {

      sr_arif=sum/npoz;

      if(f_max) {

        if(max<sr_arif) max=sr_arif;

      } else {

        f_max=1; max=sr_arif;

      }  

    }

  }

  // Вывод результата

  if(f_max)

     printf("%lf", max);

  else puts("No numbers");

  return ! f_max;

}

 

Для проверки программ обучаемых, решающих эту задачу, генерируется ее паспорт, включающий тесты, полученные с помощью эталонного решения, и ссылку на стандартный чекер для соответствующего однозначного результата в виде вещественного числа или сообщения – заданной символьной строки.

Заключение

Описана экспериментальная версия электронной системы обучения основам программирования ЭСОП, реализованная на языке С и используемая в учебном процессе. Разрабатывается ее новая версия на основе многоагентной архитектуры.

Исследуются возможности автоматизированной верификации учебных программ логическими методами [4, 66], использования в ЭСОП эвристических подходов к решению задач программирования, подобных тем, какие применяются для математических задач [67], а также различных моделей построения обучающих систем [68, 69].    

Литература

1. Медведев В. И.,  Рохлин Ф. З.,  Хохлов Д. Г. Обучающая программа по управлению данными // Автоматизированные обучающие системы: Межвузовский сборник. – Казань: Казанский авиационный институт, 1979.  С. 39 - 50.

2. Лавров С.С. Введение в программирование. – М.: Наука, 1977. 368 с.

3. Дал У.,  Дейкстра Э., Хоор К. Структурное программирование. - М.: Мир, 1975. 247 с.

4. Вирт Н. Систематическое программирование. Введение / Пер. с англ. Вик. С. Штаркмана под  ред. Ю.М. Баяковского. - М.: Мир, 1977. 184 с.

5. Вирт Н. Алгоритмы + структуры  данных = программы. - М.: Мир, 1985. 406 с.

6. Холл П. Вычислительные струк­туры. Введение в нечисленное программирование / Пер. с англ. под ред. Э.З. Любимского. - М.: Мир, 1978. 214 с.

7. Хопгуд Ф. Методы компиляции / Пер. с англ. под ред. Э.З. Любимского и В.В. Мартынюка.- М.: Мир, 1972. 160 с.   

8. Лингер Р., Миллс Х., Уитт Б. Теория и практика струк­турного программирования.- М.: Мир, 1982. 406 с.

9. Майерс Г. Надежность программного обеспечения. - М.: Мир, 1980. 360 с.

10. Медведев В. И.,  Рохлин Ф. З.,  Хохлов Д. Г.  Технология программирования: Учебное пособие. - Казань: КАИ, 1983. 56 с.

11. Медведев В. И.,  Рохлин Ф. З.,  Хохлов Д. Г.  Введение в структурное программирование на ПЛ/1: Учебное пособие. - Казань: КАИ, 1985. 84 с.

12. Основы информатики и вычислительной техники: Проб. учеб. пособие для сред. учеб. заведений. В 2-х ч.  / А.П. Ершов,  В.М. Монахов, С.А. Бешенков и др. – 2-е изд. - М.: Просвещение, Ч. 1. 1985.  96 с., Ч. 2. 1986. 143 с.

13. Вьюкова Н. И., Галатенко В. А., Ходулев А. Б..  Систематический подход к программированию. / Под. ред. Ю. М. Баяковского. - М.: Наука. Гл. ред. физ.-мат. лит., 1988. 208 с.

14. Касьянов В. Н.,  Сабельфельд В. К.  Сборник заданий  по практикуму на ЭВМ.- М.: Наука, 1986. 272 с.

15. Кушниренко А. Г.,  Лебедев Г. В. Программирование для математиков. - М.: Наука, 1988. 384 с.  

16. Основы информатики и вычислительной техники: Проб. учеб. для сред. учеб. заведений / А. Г. Кушниренко,  Г. В. Лебедев, Р.А. Сворень. – 2-е изд. - М.: Просвещение, 1991. 224 с.

17. Орлов С.А. Технологии разработки программного обеспечения: Учебник для вузов. 3-е изд. – СПб.: Питер, 2004. 527 с.

18. Software Engineering 2004: Curriculum Guidelines for Undergraduate Degree Programs in Software Engineering; Computing Curricula 2001: Computer Science / Рекомендации по преподаванию программной инженерии и информатики в университетах: пер. с англ. — М.: ИНТУИТУ «Интернет-Университет Информационных Технологий», 2007. 462 с. //  http://se.math.spbu.ru/se2004  (дата обращения: 22.06.07)

19. Керниган Б., Плоджер Ф. Инструментальные средства программирования на языке Паскаль. - М.: Радио и связь, 1985.  312 с.

20. Керниган Б., Ритчи Д. Язык программирования Си. - СПб.: “Невский диалект”, 2001. 352 с.

21. Керниган Б. У., Пайк Р. Практика программирования. - М.: “Вильямс”, 2004. 288 с.

22. Кнут Д.  Искусство программирования на ЭВМ. Т. 1. Основные алгоритмы / Пер. с англ. Г.П. Бабенко и Ю.М. Баяковского под ред. К.И. Бабенко и В.С. Штаркмана. - М.: Мир, 1976. 735 с.

23. Кнут Д.  Искусство программирования на ЭВМ. Т. 2. Получисленные алгоритмы / Пер. с англ. Г.П. Бабенко, Э.Г. Белаги и Л.В. Майорова под ред. К.И. Бабенко. - М.: Мир, 1977. 726 с.

24. Кнут Д.  Искусство программирования на ЭВМ. Т. 3. Сортировка и поиск / Пер. с англ. Н.И. Вьюковой, В.А. Галатенко и А.Б. Ходулева под ред. Ю.М. Баяковского и В.С. Штаркмана. – М.: "Мир", 1978. 846 с.

25. Кормен Т.,  Лейзерсон Ч., Ривест Р. Алгоритмы: построение и  анализ / Пер. с англ. под ред. А. Шеня. - М.: МЦНМО, 2000. 960 с.

26. Липский В. Комбинаторика  для  программистов / Пер. с польск. В.А. Евстигнеева и О.А. Логиновой под ред. А.П. Ершова. - М.: Мир, 1988, 213 с.

27. Мейер Б., Бодуэн К. Методы программирования: В 2-х томах / Пер. с франц. Ю.А. Первина под ред. и с предисловием А.П. Ершова.- М.: Мир, 1982. Т. 1. 356 с., Т. 2. 368 с.

28. Непейвода Н. Н., Скопин И. Н. Основания программирования. – Москва-Ижевск: РХД, 2003. 880 с.

29. Непейвода Н. Н. Стили и методы программирования: Учебное пособие. – М.: ИНТУИТ, 2005. 320 с.

30. Хохлов Д. Г.  Программирование на языке высокого уровня. Часть 1. Основы программирования: Учебник. - Казань: Мастер Лайн, 2009. 253 с.

31. Хохлов Д. Г.  Программирование на языке высокого уровня. Часть 2. Методы программирования: Учебник. - Казань: Мастер Лайн, 2009. 270 с.

32. Хохлов Д. Г. Системное программное обеспечение: Учебное пособие. - Казань: Мастер Лайн, 2009.  178 с.

33. Хохлов Д. Г., Захарова З.Х. Операционные системы: Учебное пособие. - Казань: Мастер Лайн, 2010.  155 с.

34. Лавров C.С. Программирование. Математические основы, средства, теория. – СПб.: БХВ-Петербург,  2001. 320 с.

35. Холстед М.Х. Начала науки о программах. М.: Финансы и статистика, 1981. 128 с.

36. Пратт Т. Языки программирования: разработка и реализация / Пер. с англ. под ред.  Ю.М. Баяковского. - М.: Мир, 1979. 574 с.

37. Соловов А.В. Электронное обучение: проблематика, дидактика, технология. - Самара: «Новая техника», 2006. 462 с.

38. Беспалько В. П. Основы теории педагогических систем. Проблемы и методы психолого-педагогического обеспечения технических обучающих систем. – Воронеж: Изд-во Воронежского университета, 1977. 303 с.

39. Касьянова Е.В. Адаптивные методы и средства поддержки дистанционного обучения программированию. Автореферат Дисс. … к.ф.-м.н., спец.  05.13.11. Новосибирск: ИСИ СО РАН, 2006. 27 с.

40. Галеев И.Х. Модель управления процессом обучения в ИОС // Международный электронный журнал "Образовательные технологии и общество (Educational Technology & Society)" - 2010. - V.13. - №3. - C.285-292. - ISSN 1436-4522. URL: http://ifets.ieee.org/russian/periodical/journal.html

41. Поспелов Д.А. Моделирование рассуждений. Опыт анализа мыслительных актов. – М.: Радио и связь, 1989. 184 с.

42. Варшавский В.И., Поспелов Д.А. Оркестр играет без дирижера. – М.: Наука, 1984. 208 с.

43. Рассел С., Норвиг П. Искусственный  интеллект: современный подход, 2-е изд.. - М.: Изд. дом «Вильямс», 2006. 1408 с.

44. Васильев С. Н., Жерлов А. К., Федосов Е. А., Федунов Б. Е. Интеллектное управление динамическими системами. М.: Наука, 2000. 352 с.

45. Голенков В. В.,  Емельянов В. В., Тарасов В. Б. Виртуальные кафедры и обучающие системы. // Новости ИИ. 2001, № 4.

46. Хохлов Д.Г., Захаров А.Н., Захарова З.Х., Подавалов А.А. Оценка  качества студенческих программ при электронном обучении программированию // Вестник КГТУ им. А.Н. Туполева. 2011. № 4. С. 153 – 156.

47. Цибульский Г.М., Герасимова Е.И., Ерошин В.В. Модели обучения автоматизированных обучающих систем // Сетевой электронный научный журнал «Системотехника», № 2, 2004.  http://systech.miem.edu.ru/2004/n2/Cibulskiy.htm  (дата обращения: 13.05.08)

48. Хохлов Д. Г. Проект электронной системы обучения программированию // Проблемы техники и технологий телекоммуникаций ПТиТТ-2011: Материалы XII Международной научно-технической конференции. Оптические технологии в телекоммуникациях ОТТ-2011: Материалы IX Международной научно-технической конференции, Казань, 21 - 24 ноября 2011 года. Казань: Изд-во Казан. гос. техн. ун-та, 2011. 560 с. С. 537 - 538.  

49. Бикмурзина А.Р., Захарова З.Х., Захаров А.Н., Хохлов Д.Г.  Электронный экзамен по программированию  // Инфокоммуникационные технологии глобального информационного общества: Сборник трудов 7-й ежегодной международной научно-практической конференции. Казань,  10-11 сентября 2009 г. Казань: ООО «Центр Оперативной  Печати», 2009. 528 с,  С. 284-289.

50. Поликарпова Н.И., Шалыто А.А. Автоматное программирование. – СПб.: Питер, 2010. 176 с.

51. Усейнов Е.С., Хохлов Д.Г. Визуализация алгоритмов // Научно-техническая конференция по вопросам информатики, вычислительной техники и информационной безопасности: Материалы конференции. Казань: Изд-во Казан. гос. техн. ун-та, 2006. С. 120 - 121.

52. Командный чемпионат мира по программированию ACM 2004/2005. Северо-Восточный Европейский регион / Под ред. проф. В. Н. Васильева и  проф. В. Г. Парфенова  – Санкт-Петербург:  СпбГУ ИТМО, 2004. – 196 с. 2008. 240 с.

53. Хохлов Д. Г. ред. Казанский турнир по программированию / под ред. Д.Г. Хохлова. – Казань: Изд-во Казан. техн. ун-та, 2010. 232 с.

54. Андреева Е. В. Принципы проверки учебных и олимпиадных задач по информатике // Информатика, 2001, № 34. С. 5-10.

55. Московские олимпиады по информатике / Под ред. Е.В. Андреевой, В.М. Гуровица и В.А. Матюхина – М.: МЦНМО, 2006. 256 с.

56. Кирюхин В.М., Окулов С.М. Методика решения задач по информатике. Международные олимпиады. − М.: БИНОМ. Лаборатория знаний, 2007. 600 с.

57. Тестирующая система университета г. Валадолид, Испания // http://online-judge.uva.es  (дата обращения  10.10.07)

58. Тестирующая система олимпиады школьников, г. Москва // http://olympiads.ru/school/system/ (дата обращения 10.10.07)

59. Хохлов Д. Г., Миннибаев Р. Ф. Интегрированная экспертная система обучения программированию. // Инновационное образование в техническом университете: Международная научно-методическая конференция. Казань: Изд-во Казан. техн. ун-та, 2004. 644 с.  С. 527 - 530.

60. Захарова З. Х., Хохлов Д. Г. Оценка программ в системе автоматизированного обучения программированию // Исследования по информатике. Вып. 10. – Казань: Отечество, 2006.  С. 159 - 162.

61. Хохлов Д. Г. Проблемы и принципы создания системы автоматизированного обучения программированию  // Информационные технологии в науке, образовании и производстве: Материалы Всероссийской научной конференции. 30-31 мая 2007 года. Казань: Изд-во Казан. гос. техн. ун-та, 2007. 796 с.  С. 636 - 639.

62. Захарова З.Х., Захаров А.Н.., Хохлов Д.Г.  Методы и алгоритмы оценки учебных программ // Инфокоммуникационные технологии глобального информационного общества: Тезисы докладов 6-й ежегодной международной научно-практической конференции. Казань, 4-5 сентября 2008 г. Казань: ООО «Центр Оперативной  Печати», 2008. 348 с.  С. 247-250.

63. Макконнелл С. Совершенный код. Мастер-класс. - М.: Русская редакция; СПб.: Питер, 2005. 896 с.

64. Хохлов Д.Г. Реализация языков программирования с помощью макропроцессора // Управляющие системы и машины. 1976. № 2. - c. 78 - 82.

65. Браун П. Макропроцессоры и мобильность программного обеспечения. - М.: Мир, 1977. 253 с.

66. Непомнящий В. А., Рякин О. М. Прикладные методы верификации программ. – М.: Радио и связь, 1988. 256 с.

67. Пойа Дж. Математическое открытие. - М.: Наука, 1976. 448 с.

68. Гаврилова Т.А., Хорошевский  В.Ф. Базы знаний интеллектуальных систем – СПб.: Питер, 2001. - 384 с.

69. Карпенко А.П. Модельное обеспечение автоматизированных обучающих систем. Обзор // Наука и образование, № 7, 2011. 63 с. //  http://technomag.edu.ru/doc/193116.html (дата обращения 26.11.11)