В окне задачника можно просматривать все имеющиеся задания данной группы (нажимая клавиши [Enter] и [Backspace], а также генерировать различные варианты исходных данных и связанных с ними контрольных (т. е. правильных") результатов. При закрытии окна программа немедленно завершит работу, и мы вернемся в редактор среды PascalABC.NET. Заметим, что при последующих запусках программы будет автоматически выбираться тот режим окна задачника, в котором оно находилось в момент его предшествующего закрытия.
Для того чтобы cгенерировать html-страницу с описанием созданной группы (это позволяет, в частности, увидеть текст преамбулы группы), достаточно в процедуре Task тестирующей программы заменить символ ?" на символ "#": Task('ExamDemo#'). Теперь при запуске этой программы на экране вместо окна задачника с заданием ExamDemo14 появится html-браузер с описанием созданной группы:
Добавим к нашей группе новые задания. Подобно заданиям, импортированным из группы ExamBegin, они будут посвящены преобразованию массивов путем перестановки их элементов. Если импортированные задания были посвящены инвертированию массива (или его части), то в новых заданиях надо будет выполнить перестановку всех пар элементов или перестановку первой и второй половины массива. Чтобы не уточнять действия в случае массивов нечетного размера, добавим в задание условие о том, что исходный массив всегда имеет четный размер. Тип элементов массива для подобных заданий является несущественным, поэтому будем обрабатывать массивы вещественных чисел. Таким образом, набор исходных данных будет иметь вид, подобный набору из задания ExamDemo2 (см. формулировку этого задания, приведенную на предыдущем рисунке). Оформление вывода результатов также не будет отличаться от оформления, требуемого в задании ExamDemo2.
Прежде чем приступать к реализации заданий, добавим в начало нашей библиотеки описание ряда вспомогательных переменных, процедур и функций.
Первая функция упрощает генерацию случайных исходных данных вещественного типа с не более чем двумя дробными знаками:
function RandR(a, b: integer): real;
begin
result := RandomN(a*100, b*100)/100;
end;
При реализации функции RandR мы использовали функцию RandomN, входящую в состав конструктора учебных заданий, начиная с версии 4.11 (функция RandomN(M, N) возвращает случайное целое число, лежащее в диапазоне M..N, включая границы диапазона). Функция RandR(a, b) (a и b -- целые) возвращает вещественное число, лежащее в диапазоне a..b и имеющее не более двух дробных знаков. Такие числа можно без потери точности записывать в текстовый файл в формате с двумя дробными знаками; таким образом, программа учащегося прочтет из файла именно то число, которое было сгенерировано при инициализации задания. Напомним, что в версии 4.11 конструктора имеется функция RandomR, также предназначенная для генерации случайных вещественных чисел, однако она не позволяет фиксировать число дробных знаков, и поэтому менее пригодна для генерации данных, предназначенных для записи в текстовые файлы.
Поскольку в обоих заданиях нам потребуется выполнять обмен значений, содержащихся в вещественных переменных, опишем процедуру, которая выполняет подобный обмен:
procedure SwapR(var a, b: real);
var
c: real;
begin
c := a;
a := b;
b := c;
end;
Следующая группа вспомогательных переменных и процедур связана с особенностью заданий типа Exam, описанной в правиле 5 (см. первый пункт данного раздела). Ввиду важности этого правила приведем его еще раз:
Набор исходных и контрольных данных надо сохранять в текстовых файлах, передавая задачнику информацию об именах этих файлов (процедурами DataS) и связывая содержимое этих файлов с разделами исходных и результирующих данных (процедурами DataFileT и ResultFileT соответственно). Таким образом, при инициализации каждого задания группы Exam надо выполнить следующие действия:
Сгенерировать имена файлов, содержащих исходные и контрольные данные (эти имена должны быть различными и меняться при каждом тестовом испытании программы; кроме того, подобно всем файлам, используемым в заданиях, они должны иметь расширение .tst). Связать созданные имена с файловыми переменными и открыть эти файлы на запись. Заполнить файлы необходимыми данными. Закрыть файлы с исходными и контрольными данными. Передать задачнику информацию об именах созданных файлов, чтобы при выполнении задания эта информация была использована при связывании файлов со стандартными потоками ввода-вывода. Передать задачнику информацию о том, что первый из созданных файлов должен быть включен в раздел исходных данных, а второй -- в раздел результатов; это, во-первых, позволит отобразить содержимое файлов в окне задачника и, во-вторых, обеспечит проверку правильности результирующего файла, созданного программой учащегося (путем его сравнения с данными контрольного файла). От условий конкретного задания будет зависеть только действие 3, связанное с заполнением файлов нужными данными. Все остальные действия являются стандартными и должны выполняться при инициализации любого задания групп Exam. Поэтому удобно оформить эти действия в виде двух вспомогательных процедур, одна из которых (StartExam) выполняет начальные действия 1-2, а другая (EndExam) -- завершающие действия 4-6. Поскольку в каждой из этих процедур необходимо использовать имена созданных файлов и связанные с ними файловые переменные, эти переменные удобно описать как глобальные:
var
f1,f2: text;
f1name, f2name: string;
procedure StartExam;
var
s: string;
begin
Str(RandomN(10000, 99999), s);
f1name := 'pt1' + s + '.tst';
f2name := 'pt2' + s + '.tst';
Assign(f1, f1name);
Rewrite(f1);
Assign(f2, f2name);
Rewrite(f2);
end;
procedure EndExam;
begin
Close(f1);
Close(f2);
DataS(f1name, 3, 1);
DataS(f2name, 45, 1);
DataFileT(f1name, 1, 5);
ResultFileT(f2name, 1, 5);
end;
Обсудим особенности этих процедур. Имена файлов, создаваемых в процедуре StartExam, имеют вид pt1#####.tst (для файла с исходными данными) и pt2#####.tst (для файла с контрольными данными), причем в позициях, помеченных символом #", располагаются цифры, выбираемые случайным образом. Тем самым обеспечиваются все требования к именам файлов: они генерируются случайным образом, имеют расширение .tst, и имя файла с исходными данными всегда отличается от имени контрольного файла. Напомним, что все файлы с расширением .tst автоматически удаляются из рабочего каталога после проверки учебного задания.
При анализе процедуры EndExam следует обратить внимание на то, что информация о содержимом исходного файла занимает всю область исходных данных (строки с первой по пятую -- см. вызов процедуры DataFileT) и, таким образом, она скрывает информацию об именах файлов, ранее выведенную в первой строке области исходных данных (см. вызовы процедур DataS). В обычном задании такая реализация была бы ошибочной, поскольку учащийся не увидел бы на экране имена файлов и не понял бы, что эти имена необходимо ввести и обработать в его программе. Однако в задании групп Exam именно такая реализация является правильной, поскольку ввод имен файлов и связывание этих файлов со стандартными потоками ввода-вывода выполняется автоматически (незаметно" для программы учащегося), и поэтому информацию об именах файлов на экране отображать не следует.