PascalABC.NET

Модуль PT4TaskMakerNET: основные компоненты


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

Определение общих характеристик группы заданий

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

  • имя группы (GroupName) — текстовая строка, содержащая от 1 до 25 символов — цифр и латинских букв, причем последний символ не может быть цифрой (если имя группы содержит более 9 символов, то она считается особой библиотечной группой, работа с которой отличается от работы с обычной группой); запрещается использовать имена стандартных групп задачника (Begin, Integer и т. д.); имена, различающиеся только регистром букв, считаются совпадающими;
  • описание группы (GroupDescription) — непустая текстовая строка с кратким описанием данной группы; при генерации полного описания группы в виде html-страницы данная строка указывается в качестве заголовка этого описания;
  • сведения об авторе (GroupAuthor) — текстовая строка с информацией о разработчике данной группы (фамилия, инициалы, год разработки, e-mail и т. п.; строка может быть пустой);
  • ключ группы (GroupKey) — непустая текстовая строка с произвольным набором символов, позволяющая в дальнейшем идентифицировать в файле результатов results.dat и results.abc те выполненные задания, которые относятся к данной группе;
  • количество заданий в группе (TaskCount) — целое число в диапазоне от 1 до 999, определяющее количество заданий в группе;
  • основная процедура группы заданий (InitTaskProc) — процедура с одним целочисленным параметром, обеспечивающая инициализацию всех заданий данной группы (параметр данной процедуры определяет номер задания в пределах группы).

Из перечисленных характеристик в дополнительном комментарии нуждается ключ группы. Если не использовать подобную характеристику, то становится невозможной идентификация группы, к которой относятся задания, выполненные учащимся. Действительно, имя задания, сохраненное в файле результатов, не позволяет однозначно его идентифицировать, поскольку ничто не мешает разработать другую группу с тем же именем и совершенно другими заданиями, после чего «подменить» ею исходную группу. Проблему решает использование ключа группы, который сложно подделать, так как он известен только разработчику группы. При успешном выполнении задания в файл результатов дополнительно записывается идентификатор группы, вычисляемый на основе ее ключа и позволяющий однозначно определить группу, к которой относится выполненное задание. Поскольку информация, связанная с идентификаторами групп, представляет интерес только для преподавателя, ознакомиться с ней можно только с помощью программы «Контрольный центр преподавателя», входящей в комплекс Teacher Pack.

Примечание. При выводе краткого описания группы в программных модулях PT4Demo и PT4Load первый символ этого описания преобразуется к нижнему регистру (поскольку текст описания располагается в этих модулях после двоеточия). Если понижать регистр первого символа не следует (в случае, если этот символ является началом фамилии или некоторой аббревиатуры, например, «ЕГЭ»), то в начале краткого описания группы надо указать дополнительный символ-метку «^» (шапочка). Пример использования символа «^» приводится в разделе «Разработка групп заданий, связанных с ЕГЭ по информатике».

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

procedure CreateGroup(GroupName, GroupDescription, GroupAuthor,
  GroupKey: string; TaskCount: integer; InitTaskProc: TInitTaskProc);

Тип TInitTaskProc определяется следующим образом:

type TInitTaskProc = procedure(n: integer);

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

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

  • в процедуре inittaskgroup определяется более одной группы заданий (в этом случае определения всех групп, кроме первой, игнорируются);
  • имя группы не соответствует имени dll-файла, в котором данная группа определяется (напомним, что имя dll-файла должно иметь вид PT4<имя группы> или PT4<имя группы><маркер локали>); при реализации группы в виде pcu-файла данное ограничение отсутствует;
  • к задачнику Programming Taskbook уже подключена группа с указанным именем;
  • имя группы не является допустимым (в частности, совпадает с именем одной из базовых групп задачника);
  • не указано краткое описание группы;
  • не указан ключ группы;
  • количество заданий не принадлежит диапазону 1–999;
  • процедурная переменная InitTaskProc содержит нулевую ссылку.

Базовые константы и процедуры для создания новых заданий


const
  xCenter = 0;
  xLeft = 100;
  xRight = 200;

Эти константы, отвечают за выравнивание данных по горизонтали: константа xCenter центрирует текст, связанный с элементом данных, относительно всей экранной строки, константы xLeft и xRight центрируют текст в пределах левой и правой половины экранной строки соответственно. Используются в качестве параметра X в процедурах групп Data и Result, а также в процедуре TaskText.


procedure CreateTask([SubgroupName: string]);

Данная процедура должна быть вызвана первой при инициализации нового задания; в качестве необязательного параметра SubgroupName указывается заголовок подгруппы, в которую включается задание (задания целесообразно разбивать на подгруппы, если их количество в группе является достаточно большим; в случае деления группы на подгруппы каждое задание рекомендуется связывать с какой-либо подгруппой). Если параметр является пустой строкой или отсутствует, то задание не связывается с какой-либо подгруппой. В окне задачника заголовок подгруппы выводится над именем задания; если подгруппа для данного задания не указана, то выводится краткое описание всей группы (определенное в параметре GroupDescription процедуры CreateGroup). При выводе краткого описания группы или заголовка подгруппы в окне задачника его текст преобразуется к верхнему регистру.

В версии 4.9 конструктора учебных заданий к первоначальным двум вариантам процедуры CreateTask были добавлены еще два варианта, предназначенные для инициализации задания по параллельному MPI-программированию.


procedure TaskText(S: string[; X, Y: integer]);

Данная процедура добавляет к формулировке задания строку S, которая располагается в строке Y (от 1 до 5) раздела формулировки задания, начиная с позиции X. Позиции нумеруются от 1; при указании параметра X следует учитывать, что ширина раздела формулировок (как и разделов исходных и результирующих данных) равна 78 символам. Кроме явного указания значения позиции X можно использовать специальные константы xCenter, xLeft и xRight; в частности, если параметр X равен 0, то строка центрируется. Рекомендуется всегда центрировать строки в формулировках заданий (как это делается в базовых группах, входящих в задачник); явное указание позиции X следует использовать лишь при выводе многострочных формул и в других случаях специального выравнивания текста. Все строки должны добавляться к формулировке последовательно; при этом если формулировка содержит 1 строку, то ее следует располагать на экранной строке с номером 3, если 2 строки — на экранных строках 2 и 4, если 3 строки — на экранных строках 2, 3 и 4, если 4 строки — на экранных строках с номерами от 2 до 5 (именно так оформляются задания в базовых группах задачника). Нарушение порядка добавления строк не проявится при отображении формулировки в окне задачника, однако приведет к неверному выводу формулировки в html-описании группы.

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

В строке S можно использовать управляющие последовательности.

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

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

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

Вариант процедуры TaskText с единственным параметром S добавлен в версию 4.11 конструктора. В этом варианте строка S должна содержать весь текст формулировки, причем строки формулировки должны разделяться символами #13, #10 или их комбинациями #13#10. Начальные и конечные пробелы в каждой строке формулировки удаляются; если в результате какая-либо строка окажется пустой, то она не учитывается. Все строки формулировки автоматически центрируются по горизонтали; их вертикальное расположение определяется количеством строк и соответствует правилам, приведенным выше (если формулировка содержит одну строку, то она располагается на экранной строке 3, и т. д.). Если требуется специальное выравнивание какой-либо строки текста, то его можно добиться за счет добавления дополнительных пробелов в начало или конец строки; чтобы эти пробелы не были удалены, первый начальный (или последний конечный) пробел должен быть экранирован символом «\» (обратная косая черта).


procedure DataB ([Cmt: string;] B: boolean; X, Y: integer);
procedure DataN([Cmt: string;] N: integer; X, Y, W: integer);
procedure DataN2([Cmt: string;] N1, N2: integer; X, Y, W: integer);
procedure DataN3([Cmt: string;] N1, N2, N3: integer; X, Y, W: integer);
procedure DataR([Cmt: string;] R: real; X, Y, W: integer);
procedure DataR2([Cmt: string;] R1, R2: real; X, Y, W: integer);
procedure DataR3([Cmt: string;] R1, R2, R3: real; X, Y, W: integer);
procedure DataC([Cmt: string;] C: char; X, Y: integer);
procedure DataS([Cmt: string;] S: string; X, Y: integer);

Процедуры группы Data добавляют к заданию элементы исходных данных. Добавленные элементы, вместе с необязательной строкой-комментарием Cmt, отображаются в разделе исходных данных, начиная с позиции X строки Y (позиции и строки нумеруются от 1; ширина экранной строки равна 78 позициям). Если используются значения параметра Y, большие 5, то разделе исходных данных будет доступна прокрутка. Как и для процедуры TaskText, параметр X может принимать три особых значения: 0 (центрирование по горизонтали относительно всей экранной строки), 100 (центрирование по горизонтали относительно левой половины экранной строки), 200 (центрирование по горизонтали относительно правой половины экранной строки). Эти значения можно также задавать с помощью констант xCenter, xLeft и xRight.

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

Для нечисловых данных ширина поля вывода полагается равной фактической ширине данных; в частности, для данных символьного типа отводятся 3 позиции, содержащие начальный апостроф, собственно символ и конечный апостроф, а для логического типа отводятся 5 позиций, достаточных для вывода названий обеих логических констант в любом используемом языке программирования. Для строки отводятся L + 2 позиции, где L — длина строки (начальная и конечная позиции используются для вывода апострофов). В зависимости от текущего языка программирования используются либо одинарные, либо двойные апострофы.

Используя процедуры группы Data, в задание можно включить до 200 различных скалярных исходных данных (при этом следует учитывать, что некоторые процедуры, например, DataN2 и DataN3, добавляют в набор исходных данных несколько элементов). Наложение различных элементов в разделе исходных данных задачником не контролируется, поэтому при размещении данных следует обращать особое внимание на то, чтобы последующие элементы не скрывали предыдущие. Кроме того, важен порядок определения исходных данных, так как именно в этом порядке данные будут передаваться программе учащегося, выполняющей это задание. Следует придерживаться стандартных правил, принятых в базовых группах задачника: данные должны перебираться по строкам (в направлении сверху вниз), а в пределах каждой строки — слева направо.

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

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

Варианты данных процедур, в которых параметр Cmt отсутствует, добавлены в версию 4.11 конструктора.


procedure DataComment(Cmt: string; X, Y: integer);

Процедура позволяет добавлять в раздел исходных данных отдельный комментарий Cmt, не связанный с каким-либо элементом исходных данных. Общее число отдельных комментариев, включаемых в разделы исходных и результирующих данных, не должно превосходить 200. Смысл параметров X и Y — тот же, что и для процедур группы Data.


procedure ResultB ([Cmt: string;] B: boolean; X, Y: integer);
procedure ResultN([Cmt: string;] N: integer; X, Y, W: integer);
procedure ResultN2([Cmt: string;] N1, N2: integer; X, Y, W: integer);
procedure ResultN3([Cmt: string;] N1, N2, N3: integer; X, Y, W: integer);
procedure ResultR([Cmt: string;] R: real; X, Y, W: integer);
procedure ResultR2([Cmt: string;] R1, R2: real; X, Y, W: integer);
procedure ResultR3([Cmt: string;] R1, R2, R3: real; X, Y, W: integer);
procedure ResultC([Cmt: string;] C: char; X, Y: integer);
procedure ResultS([Cmt: string;] S: string; X, Y: integer);

Процедуры данной группы добавляют к заданию элементы результирующих данных вместе с их контрольными значениями. Комментарии Cmt к результирующим данным сразу отображаются в разделе результатов. Контрольные значения отображаются в разделе «Пример верного решения». Смысл параметров X, Y и W — тот же, что и для процедур группы Data. Задание может содержать до 200 скалярных элементов результирующих данных.

Как и в случае исходных данных, если элемент контрольных данных не умещается в поле, выделенном для его отображения (шириной W позиций), то параметр W игнорируется, и для вывода используется минимально необходимое число экранных позиций. Однако если элемент результирующих данных, переданный в задачник программой, решающей задание, не «уложится» в размер, выделенный для соответствующего элемента контрольных данных, то в правой позиции поля вывода для этого элемента отобразится символ «*» (звездочка) красного цвета. Подобная ситуация возможна как для чисел, так и для строк (если программа учащегося выведет число или строку, размер которых больше требуемого). Для того чтобы в этой ситуации увидеть полный текст всех подобных элементов результирующих данных, следует переместить курсор мыши в раздел результатов в окне задачника; через 1–2 секунды полный текст всех данных, размер которых превышает допустимый, появится во всплывающей подсказке.

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

В параметре Cmt, содержащем текст комментария к определяемому элементу результирующих данных, можно использовать управляющие последовательности.

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

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

Варианты данных процедур, в которых параметр Cmt отсутствует, добавлены в версию 4.11 конструктора.


procedure ResultComment(Cmt: string; X, Y: integer);

Процедура позволяет добавлять в раздел результатов отдельный комментарий Cmt, не связанный с каким-либо элементом результирующих данных. Общее число отдельных комментариев, включаемых в разделы исходных и результирующих данных, не должно превосходить 200. Смысл параметров X и Y — тот же, что и для процедур группы Data.


procedure SetPrecision(N: integer);

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


procedure SetRequiredDataCount(N: integer);

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

Примером задания, в котором необходимо использовать данную процедуру, может служить задание Series10. В этом задании дается набор из N целых чисел и требуется вывести True, если данный набор содержит положительные числа, и False в противном случае. Ясно, что если при считывании элементов набора будет обнаружено положительное число, то можно сразу выводить значение True и завершать выполнение задания. Однако если при подготовке задания не указать минимально необходимое число исходных данных с помощью процедуры SetRequiredDataCount, то по умолчанию будет считаться, что для решения необходимо прочесть все исходные данные, и приведенный выше правильный вариант решения будет расценен как ошибочный (при этом будет выведено сообщение «Введены не все требуемые исходные данные»).

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

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


procedure SetTestCount(N: integer);

Процедура определяет количество N успешных тестовых испытаний программы учащегося, необходимое для того, чтобы задание было зачтено как выполненное. По умолчанию количество тестовых испытаний полагается равным 5. Значение N должно находиться в пределах от 2 до 9; при указании других вариантов параметра N выводится сообщение об ошибке.

После каждого успешного тестового испытания в окне задачника выводится сообщение (на зеленом фоне), в котором указывается номер испытания и общее число тестов, необходимых для выполнения данного задания, например: «Верное решение. Тест номер 2 (из 5)». Если при очередном тестовом испытании программы ею будет получено ошибочное решение, то счетчик успешных тестов будет сброшен в 0, и тестирование (после исправления обнаруженной ошибки) придется начинать заново.


function CurrentTest: integer;

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

При попытке подключения новой группы заданий, содержащей вызов функции CurrentTest, к задачнику более ранней версии (до 4.10 включительно) функция CurrentTest всегда возвращает 0.

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

В качестве примера приведем начальную часть процедуры, реализующей задание Array32, в котором требуется найти номер первого локального минимума. Очевидно, в данном задании следует предусмотреть особые варианты тестов, в которых первым локальным минимумом является первый или последний элемент исходного набора (а также «промежуточный вариант», в котором первый локальный минимум расположен во внутренней части набора). Ранее это обеспечивалось следующим образом (здесь n — размер исходного набора, k — порядковый номер первого локального минимума):

n := 5 + Random(6);
case Random(4) of
0: k := 1;
1: k := n;
2, 3: k := 2 + Random(n-2);
end;
…

При предусмотренных шести тестовых испытаниях вполне могло оказаться, что функция Random(4) ни разу не примет значения 0 или 1, и тем самым один или оба особых случая не будут протестированы. Возможна (хотя и менее вероятна) ситуация, при которой в течение всех испытаний ни разу не будут получены значения 2 и 3; тем самым не будет испытан «промежуточный вариант». В то же время было бы желательно, чтобы ошибка алгоритма, связанная с неверной обработкой одной из возможных ситуаций, была выявлена задачником автоматически, до просмотра преподавателем текста программы.

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

n := RandomN(5, 10);
case Random(4) of
0: k := 1;
1: k := n;
2, 3: k := RandomN(2, n-1);
end;
case CurrentTest of
2: k := n;
3: k := RandomN(2, n-1);
5: k := 1;
end;
…

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

Заметим, что в новом варианте была также использована функция RandomN, добавленная в версию 4.11 конструктора.

После включения функции CurrentTest в конструктор были соответствующим образом модифицированы (без изменения формулировок) все группы заданий, включенные в базовый вариант задачника версии 4.11.


function RandomN(M, N: integer): integer;
function RandomR(A, B: real): real;

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

Функция RandomN(M, N) возвращает псевдослучайное целое число, лежащее в диапазоне от M до N–1 включительно. Если указанный диапазон пуст, то функция возвращает M.

Функция RandomR(A, B) возвращает псевдослучайное вещественное число, лежащее на полуинтервале [AB). Если указанный полуинтервал пуст, то функция возвращает A.

При генерации заданий можно применять и стандартную функцию Random языка Pascal.


function Center(I, N, W, B: integer): integer;

Вспомогательная функция, которая позволяет размещать по центру экранной строки набор из N элементов данных одинаковой ширины. Эта функция возвращает горизонтальную координату, начиная с которой следует выводить I-й элемент набора (I меняется от 1 до N) при условии, что ширина каждого элемента равна W позициям, а между элементами надо указывать B пробелов. Функция Center обычно используется в качестве параметра X в процедурах групп Data и Result при выводе однотипных наборов данных (в частности, элементов массива).

В качестве примера приведем фрагмент, обеспечивающий формирование и вывод в разделе исходных данных массива вещественных чисел:

n := RandomN(2, 10);
DataN('N = ', n, 0, 2, 1);
for i := 1 to n do
begin
  a[i] := RandomR(-9.99, 9.99);
  DataR(a[i], Center(i, n, 5, 1), 4, 5);
end;

Вначале (во второй строке области исходных данных) выводится размер N массива, определяемый с помощью датчика случайных чисел и принимающий значения в диапазоне от 2 до 10 (он снабжается комментарием «N = »). Затем (в четвертой строке) выводятся сами элементы массива, причем благодаря использованию функции Center весь список выравнивается относительно центра экранной строки независимо от количества элементов. Целые части всех элементов лежат в диапазоне от -9 до 9, т. е. представляются одной цифрой, одна позиция отводится под знак числа, еще одна — под отображение десятичного разделителя-точки; наконец, по умолчанию указываются два дробных знака, поэтому для каждого элемента следует выделить 5 экранных позиций; это число указывается дважды: как второй параметр функции Center и как последний параметр процедуры DataR. Промежуток между элементами полагается равным 1 экранной позиции (это последний, четвертый параметр функции Center).

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

Прокрутка разделов исходных данных и результатов

Начиная с версии 4.9 конструктора учебных заданий, для процедур групп Data и Result, в том числе DataComment и ResultComment, в качестве параметра Y разрешено указывать значение, превышающее 5. Если значение параметра Y для некоторого элемента раздела исходных данных превышает 5, то этот элемент размещается в строке с указанным номером, а в разделе исходных данных становится доступной прокрутка. Аналогичным образом прокрутка будет доступна в разделе результатов, если хотя бы один элемент этого раздела помещен в него процедурой с параметром Y, превышающим 5. Если оба раздела допускают прокрутку, то она выполняется независимо. Прокрутку в любом разделе можно выполнять с помощью клавиатуры или мыши; в последнем случае следует использовать полосы прокрутки, расположенные справа от прокручиваемого раздела. Прокрутка может также выполняться с помощью колесика мыши.

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

Возможность прокрутки разделов исходных и результирующих данных добавлена, прежде всего, для использования в заданиях по параллельному программированию. Однако она может оказаться полезной и в других случаях, например, при использования в качестве исходных данных нескольких двумерных массивов или массива строк. Заметим, что ни в одном из 1300 заданий, входящих в базовый набор задачника Programming Taskbook, прокрутка разделов исходных и результирующих данных не используется. Большое количество заданий с прокруткой разделов исходных и результирующих данных содержится в группе Align, входящей в задачник по строковым алгоритмам биоинформатике Programming Taskbook for Bio.

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

Импортирование существующих заданий в новую группу


procedure UseTask(GroupName: string; TaskNumber: integer);

Данная процедура позволяет импортировать в создаваемую группу задание с номером TaskNumber из группы GroupName. Она обычно вызывается непосредственно в основной процедуре группы. Если импортируемое задание не найдено, то при попытке его запуска в окне задачника выводится сообщение «Задание не реализовано для текущего языка программирования», и этот же текст, выделенный курсивом, указывается в html-описании группы после имени, которое должно быть связано с импортированным заданием.

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

В параметре GroupName после имени группы можно дополнительно указывать поправку для вычисления ссылки на другое задание (поправка является целым числом и отделяется от имени группы символом #). Например, если в группу Demo в качестве задания Demo10 импортируется задание Proc46, а в качестве Demo11 — задание Proc49, ссылающееся на Proc46, то при импортировании задания Proc49 необходимо указать поправку, равную 2. Если этого не сделать, то в формулировке задания Demo11 будет указана ссылка не на задание Demo10, а на задание Demo8 (поскольку оно находится «на том же расстоянии» от задания Demo11, что и задание Proc46 относительно задания Proc49). Добавление поправки 2 должно быть оформлено следующим образом: UseTask('Proc#2',49).

Документирование группы заданий

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

Комментарии не отображаются в окне задачника, но включаются в html-описание группы. Они располагаются между заголовком группы (подгруппы) и формулировками заданий. Таким образом, эти комментарии представляют собой преамбулы к группе или ее подгруппам.

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

Для определения преамбул предназначены следующие процедуры.


procedure CommentText(S: string);

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


procedure UseComment(GroupName: string[; SubgroupName: string]);

Процедура UseComment добавляет к текущей преамбуле текст преамбулы подгруппы SubgroupName группы GroupName или, если параметр SubgroupName является пустой строкой или отсутствует, текст преамбулы самой группы GroupName. Этот текст отделяется от предыдущего текста преамбулы пробелом. Регистр символов в параметрах GroupName и SubgroupName может быть произвольным.

Если группа с именем GroupName не найдена или в ней отсутствует подгруппа SubgroupName, то процедура не выполняет никаких действий; сообщение об ошибке в этом случае не выводится.

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


procedure Subgroup(SubgroupName: string);

Данная процедура устанавливает режим добавления текста к преамбуле подгруппы SubgroupName текущей группы. Этот режим сохраняется до следующего вызова данной процедуры или до завершения определения текущей группы заданий (определение группы, создаваемой в виде dll-файла, завершается при выходе из процедуры inittaskgroup).

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

Константы и функции для определения текущего состояния задачника


const
  lgPascal = $0001;
  lgVB = $0002;
  lgCPP = $0004;
  lg1C = $0040;
  lgPython = $0080;
  lgCS = $0100;
  lgVBNET = $0200;
  lgPascalNET = $0400;
  lgJava = $10000;
  lgWithPointers = $003D;
  lgWithObjects = $FFF80;
  lgNET = $FF00;
  lgPascalABCNET = $0401;
  lgAll = $FFFFFF;

Данные константы, совместно с описываемой далее функцией CurrentLanguage, позволяют определить язык программирования, на который в данный момент (т. е. в момент инициализации текущей группы заданий) настроен задачник. Константы lgPascal, lgVB, lgCPP, lgCS, lgVBNET, lgPascalABCNET, lg1C, lgPython, lgJava соответствуют конкретному языку из числа тех, которые доступны в текущей версии задачника (Pascal, Visual Basic, C++, C#, Visual Basic .NET, PascalABC.NET, 1С:Предприятие, Python, Java). Эти константы являются битовыми флагами. Константа lg1C появилась в версии 4.9 конструктора учебных заданий (в связи с реализацией комплекса PT for 1C — варианта задачника для системы 1С:Предприятие), константа lgPython — в версии 4.10 (в связи с реализацией варианта задачника для языка Python), константа lgJava — в версии 4.11 (в связи с реализацией варианта задачника для языка Java).

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

  • lgAll — любой язык,
  • lgNET — язык платформы .NET (языки C# и Visual Basic .NET),
  • lgWithPointers — язык, для которого можно разрабатывать группы заданий на обработку динамических структур с применением указателей (языки Pascal и C++),
  • lgWithObjects — язык, для которого можно разрабатывать группы заданий на обработку динамических структур с применением объектов (все языки платформы .NET, а также Python и Java).

Особое место занимает язык, реализованный в системе PascalABC.NET, поскольку в нем объединяются свойства обычного языка Pascal и языка платформы .NET. Данному языку соответствует комбинация флагов lgPascal и lgPascalNET; это, в частности, означает, что он принадлежит одновременно к категориям lgWithPointers, lgWithObjects и lgNET. Для языка PascalABC.NET предусмотрена также именованная константа lgPascalABCNET.


function CurrentLanguage: integer;

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

CurrentLanguage and lgNET <> 0

При использовании задачника совместно с системой PascalABC.NET функция CurrentLanguage возвращает значение lgPascalABCNET.


function CurrentLocale: string;

Функция возвращает строку, соответствующую текущей локали, т. е. текущему языку интерфейса, используемому в задачнике. В версии 4.11 конструктора учебных заданий возможными возвращаемыми значениями функции CurrentLocale являются 'ru' (русский вариант задачника) и 'en' (английский вариант).


function CurrentVersion: string;

Данная функция добавлена в версию 4.10 конструктора учебных заданий. Она возвращает номер текущей версии задачника в виде строки числа формата 'd.dd'. Например, в случае версии 4.11 возвращается строка '4.11'. Для версий, предшествующих версии 4.10, функция возвращает строку '4.00'.

Образцы слов и предложений

Приведенные ниже элементы конструктора PT4TaskMaker позволяют получить доступ к встроенным в него образцам текстовых исходных данных: словам (Word), предложениям (Sentence) и многострочным текстам (Text).


const
  SampleError = '#ERROR?';
  MaxLineCount = 50;

function WordCount: integer;
function SentenceCount: integer;
function TextCount: integer;
function WordSample(N: integer): string;
function SentenceSample(N: integer): string;
function TextSample(N: integer): string;

function EnWordCount: integer;
function EnSentenceCount: integer;
function EnTextCount: integer;
function EnWordSample(N: integer): string;
function EnSentenceSample(N: integer): string;
function EnTextSample(N: integer): string;

Функции WordSample, SentenceSample и TextSample возвращают текстовые данные, соответствующие текущей локали, т. е. текущему языку интерфейса, используемому в задачнике (см. функцию CurrentLocale): для русского варианта задачника возвращаются русские данные, для английского — английские. Варианты этих функций, снабженные префиксом En, возвращают английские текстовые данные в любом варианте задачника.

Функции, оканчивающиеся словом Count, возвращают количество соответствующих элементов данных. В версии 4.11, конструктора учебных заданий, как и в его предыдущих версиях, доступно 116 слов, 61 предложение и 85 текстов как на русском, так и на английском языке.

Функции WordSample/EnWordSample и SentenceSample/EnSentenceSample возвращают соответственно слово или предложение с индексом N (индексирование проводится от 0).

Функция TextSample/EnTextSample возвращает строку, связанную с многострочным текстом, имеющим индекс N (индексирование также проводится от 0). При этом между соседними строками этого текста располагаются символы #13#10 (маркеры конца строки). В конце текста маркер конца строки отсутствует, число строк в тексте не превышает значения константы MaxLineCount. Любой текст состоит из нескольких абзацев; между абзацами текста помещается одна пустая строка, отступы в начале абзацев («красная строка») не используются. В тексте не используются также переносы слов.

Если параметр N является недопустимым, то все функции возвращают особую строку, равную константе SampleError.

Буква «ё» в русских текстовых данных не используется.

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

  • слова, начинающиеся и оканчивающиеся одной и той же буквой;
  • слова, содержащие три одинаковые буквы (в русском наборе — три буквы «А», в английском наборе — три буквы «E»).

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

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