Задание, которое мы реализуем, дублирует задание Dynamic30, посвященное преобразованию односвязного списка в двусвязный (подгруппа Динамические структуры данных: двусвязный список"). Оформим два варианта этого задания в виде процедур MakerDemo8 и MakerDemo8Obj:
var WrongNode: TNode;
procedure MakerDemo8Data;
var
i, n: integer;
p, p1, p2: PNode;
begin
if RandomN(1, 4) = 1 then
n := 1
else
n := RandomN(2, 9);
case CurrentTest of
2: n := 1;
4: n := RandomN(2, 9);
end;
new(p1);
p1^.Data := RandomN(10, 99);
p1^.Prev := nil;
p2 := p1;
for i := 2 to n do
begin
new(p);
p^.Data := RandomN(10, 99);
p^.Prev := p2;
p2^.Next := p;
p2 := p;
end;
p2^.Next := nil;
SetPointer(1, p1);
SetPointer(2, p2);
ResultP('Последний элемент: ', 2, 0, 2);
ResultList(1, 0, 3);
ShowPointer(2);
DataP(1, 0, 2);
p := p1;
for i := 1 to n do
begin
p^.Prev := @WrongNode;
p := p^.Next;
end;
DataList(1, 0, 3);
ShowPointer(1);
end;
procedure MakerDemo8;
begin
CreateTask('Динамические структуры данных: двусвязный список');
TaskText('Дан указатель~{P}_1 на начало непустой цепочки ' +
'элементов-записей типа TNode,', 0, 1);
TaskText('связанных между собой с помощью поля Next. Используя ' +
'поле Prev записи TNode,', 0, 2);
TaskText('преобразовать исходную (\Iодносвязную\i) цепочку ' +
'в \Iдвусвязную\i, в которой каждый', 0, 3);
TaskText('элемент связан не только с последующим элементом ' +
'(с помощью поля Next),', 0, 4);
TaskText('но и с предыдущим (с помощью поля Prev). Поле Prev ' +
'первого элемента положить', 0, 5);
TaskText('равным \N. Вывести указатель на последний элемент ' +
'преобразованной цепочки.', 0, 0);
MakerDemo8Data;
end;
procedure MakerDemo8Obj;
begin
CreateTask('Динамические структуры данных: двусвязный список');
TaskText(
'Дана ссылка~{A}_1 на начало непустой цепочки элементов-объектов типа Node,'#13 +
'связанных между собой с помощью своих свойств Next. Используя свойства Prev'#13 +
'данных объектов, преобразовать исходную (\Iодносвязную\i) цепочку в \Iдвусвязную\i,'#13 +
'в которой каждый элемент связан не только с последующим элементом (с помощью'#13 +
'свойства Next), но и с предыдущим (с помощью свойства Prev). Свойство Prev'#13 +
'первого элемента положить равным \O. Вывести ссылку~{A}_2 на последний'#13 +
'элемент преобразованной цепочки.'
);
SetObjectStyle;
MakerDemo8Data;
end;
Анализируя приведенные варианты процедур, легко заметить, что они отличаются лишь деталями формулировки задания. Алгоритмы генерации исходных и контрольных данных для традиционного и объектного вариантов совпадают, поэтому они выделены в отдельную вспомогательную процедуру MakerDemo8Data. В то же время представления динамических структур и связанных с ними указателей или объектов будут отличаться (см. рисунки, приведенные ниже). Необходимые корректировки в представлении динамических структур выполняются задачником автоматически, с учетом используемого языка программирования.
Однако для языка PascalABC.NET требуемую настройку необходимо выполнить явно, так как в нем можно использовать оба варианта представления динамических структур: традиционный (как для обычного Паскаля в системах Delphi и Free Pascal Lazarus) и объектный (как в языках C#, Visual Basic .NET, Python и Java). Для того чтобы представление динамических данных при выполнении задания в среде PascalABC.NET соответствовало объектному варианту, следует в начале процедуры, реализующей задание (перед вызовом любых процедур, связанных с указателями и динамическими структурами), вызвать специальную процедуру без параметров SetObjectStyle. Для остальных языков данная процедура не выполняет никаких действий.
Обратите внимание на возможность использования в формулировке задания более 5 экранных строк. Строки, которые не умещаются в области формулировки задания, следует добавлять к заданию процедурой TaskText, указывая в качестве последнего параметра процедуры число 0 (см. процедуру MakerDemo8). Еще проще задавать длинные" формулировки заданий с помощью нового варианта процедуры TaskText с единственным строковым параметром, содержащим все строки формулировки (см. процедуру MakerDemo9). При наличии подобных строк в окне задачника (если окно находится в режиме с фиксированной компоновкой) слева от области формулировки появятся кнопки, обеспечивающие прокрутку формулировки задания; кроме этих кнопок для прокрутки можно также использовать стандартные клавиши, в частности, клавиши со стрелками.
Для того чтобы имя нулевого указателя (или объекта) соответствовало используемому языку программирования, в формулировке задания применяются управляющие последовательности \N (имя нулевого указателя) и \O (имя нулевого объекта). Для языка PascalABC.NET обе эти последовательности генерируют текст nil.
Достаточно часто алгоритмы, разработанные учащимися для обработки динамических структур данных, дают неверные результаты в случае особых (хотя и допустимых) структур, например, состоящих только из одного элемента. Поэтому желательно предусмотреть появление подобных структур в тестовых наборах исходных данных. В наших заданиях исходный список, состоящий из одного элемента, будет предлагаться программе учащегося в среднем один раз при каждых четырех тестовых испытаниях. Кроме того, благодаря использованию функции CurrentTest, появившейся в версии 4.11 конструктора, вариант списка с единственным элементом будет предложен программе учащегося для обработки в тесте номер 2, а вариант списка с более чем одним элементом -- в тесте номер 4. Таким образом, можно гарантировать, что при прохождении набора из 5 тестовых испытаний программе будут предложены как стандартные", так и "особые" наборы исходных данных.
При формировании односвязной структуры неиспользуемые поля Prev для каждого элемента структуры следует положить равными адресу фиктивного" элемента (в нашем случае -- переменной WrongNode), не связанного с данной структурой. Заметим, что для всех элементов, кроме первого, значения поля Prev можно было бы положить равными nil, однако это не подходит для первого элемента: если поле Prev первого элемента будет равно nil, то слева от него будет выведен "лишний" (в данной ситуации) текст nil<.
Характерной особенностью разработки заданий на динамические структуры является обратный порядок создания этих структур: вначале создаются контрольные структуры (которые сразу передаются в задачник), а затем они преобразуются в соответствующие исходные структуры, которые должны не только передаваться в задачник, но и оставаться в памяти, чтобы в дальнейшем их можно было использовать в программе учащегося, выполняющей это задание.