Читаем Описание языка PascalABC.NET полностью

Команда

Описание

Окно задачника

Html-страница

Ссылки на другие задания данной группы

\число

Имя задания из текущей группы с номером, меньшим текущего на величину указанного десятичного числа (или *********, если результирующий номер оказывается меньшим 1)

\0число

Имя задания из текущей группы с номером, большим текущего на величину указанного десятичного числа (или *********, если результирующий номер оказывается большим 999)

Пробелы

\,

Малый неразрывный пробел

Игнорируется

&

\;

Средний неразрывный пробел

Пробел

&

~

Обычный неразрывный пробел

Пробел

&

\q

Большой пробел

Игнорируется

Пробел& Пробел&

\Q

Двойной большой пробел

Игнорируется

2 копии большого пробела

Символы

\\

Обратная косая черта (\)

\

\

\&

Амперсанд (&)

&

&

\{

Открывающая фигурная скобка ({)

{

{

\}

Закрывающая фигурная скобка (})

}

}

\~

Символ волна" (~)

~

~

\^

Символ шапочка" (^)

^

^

\_

Символ подчеркивания (_)

_

_

\.

Многоточие (…)

...

(три точки)

\=

Длинное тире (—)

\:

Символ диапазона, или короткое тире (–)

\-

Символ минус" (−)

\<

Открывающие угловые кавычки ()

«

«

\>

Закрывающие угловые кавычки (")

"

»

\*

Символ умножения (·)

·

·

\+

Символ плюс-минус" (±)

±

±

\o

Символ градуса (°)

°

°

\x

Символ косой крест" (×)

×

×

\a

Греческая буква альфа" (α)

α

α

\p

Греческая буква пи" (π)

π

π

\e

Греческая буква эпсилон" (ε)

ε

ε

\l

Символ меньше или равно" (≤)

\g

Символ больше или равно" (≥)

\n

Символ не равно" (≠)

\X

Символ bullet" (•)

\hXX, где XX -- двузначное 16-ричное число

Символ с кодом XX из второй половины кодовой таблицы для западноевропейских языков ANSI Latin-1 (кодовая страница 1252)

Соответствующий символ

&#DDD;

(DDD -- десятичный код соответствующего символа в кодировке Unicode)

\HXX, где XX -- двузначное 16-ричное число

Символ с кодом XX Windows-шрифта Symbol

Соответствующий символ

&#<emphasis>DDD</emphasis>;</p><p><emphasis>(DDD -- десятичный код соответствующего символа в кодировке Unicode)</emphasis></p><p><emphasis>или</emphasis></p><p><font face="Symbol">&#<emphasis>DDD</emphasis>;</font></p><p><emphasis>(DDD -- десятичное число, равное 16-ричному числу XX)</emphasis></p></td></tr><tr><td colspan="olspan = 4"><p><strong>Элементы текста, зависящие от текущего языка программирования</strong></p></td></tr><tr><td><p>\R</p></td><td><p>Метка начала квадратного корня</p></td><td><p>Sqrt(</p><p><emphasis>(Pascal, PascalABC.NET, VB.NET, C#)</emphasis></p><p>sqrt(</p><p><emphasis>(C++, Python, Java)</emphasis></p><p>Sqr(</p><p><emphasis>(Visual Basic версий 5 и 6)</emphasis></p></td><td><p>(</p></td></tr><tr><td><p>\r</p></td><td><p>Метка конца квадратного корня</p></td><td><p>)</p></td><td><p>)<sup>1/2</sup></p></td></tr><tr><td><p>\t</p></td><td><p>Логическая константа True"</p></td><td colspan="olspan=2"><p>True <emphasis>(Pascal, PascalABC.NET, Visual Basic, VB.NET, Python)</emphasis></p><p>true <emphasis>(C++, C#, Java)</emphasis></p></td></tr><tr><td><p>\f</p></td><td><p>Логическая константа False"</p></td><td colspan="olspan=2"><p>False <emphasis>(Pascal, PascalABC.NET, Visual Basic, VB.NET, Python)</emphasis></p><p>false <emphasis>(C++, C#, Java)</emphasis></p></td></tr><tr><td><p>\N</p></td><td><p>Нулевой указатель</p></td><td colspan="olspan=2"><p>nil <emphasis>(Pascal)</emphasis></p><p>NULL <emphasis>(C++)</emphasis></p></td></tr><tr><td><p>\O</p></td><td><p>Нулевая ссылка для объекта</p></td><td colspan="olspan=2"><p>null <emphasis>(C#, Java)</emphasis></p><p>Nothing <emphasis>(VB.NET)</emphasis></p><p>nil <emphasis>(PascalABC.NET)</emphasis></p><p>None <emphasis>(Python)</emphasis></p></td></tr><tr><td><p>\d</p></td><td><p>Имя функции для освобождения ресурсов, выделенных объекту</p></td><td colspan="olspan=2"><p>Dispose <emphasis>(PascalABC.NET, C#, VB.NET)</emphasis></p><p>dispose <emphasis>(Python, Java)</emphasis></p></td></tr><tr><td colspan="olspan = 4"><p><strong>Индексы</strong></p></td></tr><tr><td><p>_<emphasis>символ</emphasis></p></td><td><p>Односимвольный нижний индекс</p></td><td><p><emphasis>Cимвол выводится как нижний индекс</emphasis></p></td><td><p><sub><emphasis>символ</emphasis></sub></p></td></tr><tr><td><p>^<emphasis>символ</emphasis></p></td><td><p>Односимвольный верхний индекс</p></td><td><p><emphasis>Cимвол выводится как верхний индекс</emphasis></p></td><td><p><sup><emphasis>символ</emphasis></sup></p></td></tr><tr><td><p>_{</p></td><td><p>Метка начала многосимвольного нижнего индекса</p></td><td><p><emphasis>Переход в режим нижнего индекса</emphasis></p></td><td><p><sub></p></td></tr><tr><td><p>^{</p></td><td><p>Метка начала многосимвольного верхнего индекса</p></td><td><p><emphasis>Переход в режим верхнего индекса</emphasis></p></td><td><p><sup></p></td></tr><tr><td><p>}</p></td><td><p>Метка конца текущего (верхнего или нижнего) многосимвольного индекса</p></td><td><p><emphasis>Выход из режима индекса</emphasis></p></td><td><p></sub></p><p><emphasis>или</emphasis></p><p></sup></p></td></tr><tr><td colspan="olspan = 4"><p><strong>Выделение (в окне задачника любой режим выделения приводит к выделению полужирным шрифтом)</strong></p></td></tr><tr><td><p>\B</p></td><td><p>Метка начала полужирного выделения</p></td><td><p><emphasis>Переход в режим выделения</emphasis></p></td><td><p><b></p></td></tr><tr><td><p>\b</p></td><td><p>Метка конца полужирного выделения</p></td><td><p><emphasis>Выход из режима выделения</emphasis></p></td><td><p></b></p></td></tr><tr><td><p>\I</p></td><td><p>Метка начала курсивного выделения</p></td><td><p><emphasis>Переход в режим выделения</emphasis></p></td><td><p><i></p></td></tr><tr><td><p>\i</p></td><td><p>Метка конца курсивного выделения</p></td><td><p><emphasis>Выход из режима выделения</emphasis></p></td><td><p></i></p></td></tr><tr><td><p>\S</p></td><td><p>Метка начала специального выделения</p></td><td><p><emphasis>Переход в режим выделения</emphasis></p></td><td><p><span class="ptSpecial"></p></td></tr><tr><td><p>\s</p></td><td><p>Метка конца специального выделения</p></td><td><p><emphasis>Выход из режима выделения</emphasis></p></td><td><p></span></p></td></tr><tr><td colspan="olspan = 4"><p><strong>Дополнительное форматирование html-страниц (в окне задачника данные управляющие последовательности игнорируются)</strong></p></td></tr><tr><td><p>{</p></td><td><p>Метка начала выделения переменной</p></td><td><p> </p></td><td><p><i></p></td></tr><tr><td><p>}</p></td><td><p>Метка конца выделения переменной</p></td><td><p> </p></td><td><p></i></p></td></tr><tr><td><p>\M</p></td><td><p>Метка начала моноширинного текста</p></td><td><p> </p></td><td><p><tt></p></td></tr><tr><td><p>\m</p></td><td><p>Метка конца моноширинного текста</p></td><td><p> </p></td><td><p></tt></p></td></tr><tr><td><p>\|</p></td><td><p>Разрыв строки</p></td><td><p> </p></td><td><p><br></p></td></tr><tr><td><p>\P</p></td><td><p>Начало нового абзаца</p></td><td><p> </p></td><td><p><emphasis>Завершается предыдущий абзац и создается абзац стиля</emphasis> ptTaskContinue <emphasis>(при использовании в формулировке задания) или</emphasis> ptComment <emphasis>(при использовании в тексте преамбулы)</emphasis></p></td></tr><tr><td><p>\[</p></td><td><p>Метка начала абзаца с центрированием</p></td><td><p> </p></td><td><p><emphasis>Завершается предыдущий абзац и создается абзац стиля</emphasis> ptTaskCenter <emphasis>(при использовании в формулировке задания) или</emphasis> ptCommentCenter <emphasis>(при использовании в тексте преамбулы)</emphasis></p></td></tr><tr><td><p>\]</p></td><td><p>Метка конца абзаца с центрированием</p></td><td><p> </p></td><td><p><emphasis>Завершается предыдущий абзац и создается абзац стиля</emphasis> ptTaskContinue <emphasis>(при использовании в формулировке задания) или</emphasis> ptCommentContinue <emphasis>(при использовании в тексте преамбулы)</emphasis></p></td></tr><tr><td><p>\(</p></td><td><p>Метка начала абзаца с отступом</p></td><td><p> </p></td><td><p><emphasis>Завершается предыдущий абзац и создается абзац стиля</emphasis> ptTaskQuote <emphasis>(при использовании в формулировке задания) или</emphasis> ptCommentQuote <emphasis>(при использовании в тексте преамбулы)</emphasis></p></td></tr><tr><td><p>\)</p></td><td><p>Метка конца абзаца с отступом</p></td><td><p> </p></td><td><p><emphasis>Завершается предыдущий абзац и создается абзац стиля</emphasis> ptTaskContinue <emphasis>(при использовании в формулировке задания) или</emphasis> ptCommentContinue <emphasis>(при использовании в тексте преамбулы)</emphasis></p></td></tr><tr><td><p>\J</p></td><td><p>Метка начала режима выравнивания по столбцам (после нее указывается последовательность символов r, l, c, которая должна оканчиваться символом &)</p></td><td><p> </p></td><td><p><table></p></td></tr><tr><td><p>\j</p></td><td><p>Метка конца режима выравнивания по столбцам</p></td><td><p> </p></td><td><p></table></p></td></tr><tr><td><p>&</p></td><td><p>Переход к новому столбцу в режиме выравнивания по столбцам</p></td><td><p> </p></td><td><p><emphasis>Добавляется тег</emphasis> <td> <emphasis>с соответствующим выравниванием; для первого столбца предварительно указывается тег</emphasis> <tr></p></td></tr></table><empty-line/><subtitle>Дополнительные сведения об использовании управляющих последовательностей</subtitle><empty-line/><p>Необходимость в специальных командах для генерации ссылок на другие задания группы объясняется тем, что любое имеющееся задание может быть импортировано в группу с другим именем (с помощью процедуры UseTask), и поэтому все ссылки на другие задания этой группы также потребуется откорректировать, указав в них новое имя группы. Разумеется, в подобной ситуации необходимо переносить в новую группу <emphasis>все</emphasis> задания, содержащие ссылки друг на друга. Следует заметить, что <emphasis>разность</emphasis> между номерами ссылающихся друг на друга заданий не обязана быть такой же, как в исходной группе заданий. Если в новой группе задания находятся на другом расстоянии" друг от друга, то для указания правильной ссылки достаточно внести соответствующую <emphasis>поправку</emphasis> в параметр процедуры UseTask.</p><p>Наличие нескольких видов <emphasis>неразрывных пробелов</emphasis>, не различающихся в тексте заданий и html-страниц, связано с планируемой в дальнейшем возможностью генерации текста заданий в других форматах (в частности, в формате системы TeX, в котором данные виды пробелов различаются). Приведем рекомендации по использованию неразрывных пробелов:</p><p>вокруг символов =, <, > указывается обычный неразрывный пробел ~; исключением являются фрагменты текста в скобках вида ( 0), в которых рекомендуется использовать малый пробел: (\,0); неразрывный пробел ~ указывается также между текстом и переменной: стороны~{a} и~{b}; вокруг символов + и - ставится средний пробел \;; символы умножения \* и деления / пробелами не обрамляются; исключением служит ситуация, когда слева и справа от символа деления указываются прописные буквы; в этом случае желательно использовать обрамление малыми пробелами. Приведем пример оформления формул (данный пример взят из задания Begin39; обратите внимание на выделение переменных с помощью фигурных скобок, а также на команды, обеспечивающие вывод индексов, выделение квадратного корня и центрирование формулы):</p><empty-line/><p><code>TaskText('Найти корни \Iквадратного уравнения\i ' +</code></p><p><code>'{A}\*{x}^2\;+\;{B}\*{x}\;+\;{C}~=~0, заданного', 0, 1);</code></p><p><code>TaskText('своими коэффициентами~{A}, {B}, {C} ' +</code></p><p><code>'(коэффициент~{A} не равен~0), если известно,', 0, 2);</code></p><p><code>TaskText('что дискриминант уравнения положителен. ' +</code></p><p><code>'Вывести вначале меньший, а затем',0,3);</code></p><p><code>TaskText('больший из найденных корней. Корни квадратного ' +</code></p><p><code>'уравнения находятся по формуле', 0, 4);</code></p><p><code>TaskText('\[{x}_{1,\,2}~=~(\-{B}\;\+\;\R{D}\r)/(2\*{A}),\] ' +</code></p><p><code>'где {D}~\= \Iдискриминант\i, ' +</code></p><p><code>'равный {B}^2\;\-\;4\*{A}\*{C}.', 0, 5);</code></p><empty-line/><p>В результате обработки данной формулировки задания в окне задачника будет выведен текст:</p><p><image l:href="#pt4make15.png"/></p><p>В html-описании этот же текст будет отформатирован следующим образом:</p><p><image l:href="#pt4make16.png"/></p><p>Для указания кавычек в тексте задания следует использовать управляющие последовательности \< и \>.</p><p>Управляющие последовательности \t, \f, \N, \O для логических констант, нулевых указателей и объектов генерируют текст, зависящий от выбранного в данный момент языка программирования.</p><p>Обычные пробелы, указанные после управляющих последовательностей \q, \Q, \P, \[, \(, \], \), \| и &, учитываются только в тексте задачника (и пропускаются в тексте html-страниц).</p><p>Режим специального выделения, устанавливаемый парными командами \S и \s, в окне задачника приводит к выделению полужирным шрифтом, а в html-описании обеспечивает выделение фрагмента текста, аналогичное выделению, используемому для <emphasis>имени задания</emphasis> в начале его формулировки (в приведенном выше фрагменте html-описания так выделено имя задания Begin39"). Данный режим рекомендуется использовать для выделения <emphasis>заголовков</emphasis>, размещаемых в начале абзаца (например, если формулировка задания завершается абзацем, содержащим указание, то с помощью специального выделения целесообразно выделить текст "Указание" в начале этого абзаца).</p><p>Команды выделения переменной { и } не влияют на ее вид в окне задачника, но обеспечивают ее выделение курсивом в тексте html-страницы. В индексах команды выделения переменной не учитываются, а любые <emphasis>латинские</emphasis> буквы в них автоматически выделяются курсивом.</p><p>В односимвольных индексах нельзя указывать управляющие последовательности для вывода специальных символов, поэтому при необходимости применения в индексах специальных символов следует использовать режим многосимвольных индексов. Метки индексов не могут быть вложенными. В индексах не допускается использование меток выделения \I, \B, \S и наоборот, внутри выделенного текста не допускается указывать индексы.</p><p>Выделенные фрагменты не могут содержать меток выделения другого вида. Режим индексов и выделения, заданный в одной процедуре TaskText или CommentText, не переносится на текст, определяемый при последующих вызовах этих процедур. Если в тексте отсутствуют команды завершения текущего режима (индексов или выделения), то режим автоматически завершается при достижении конца текста, определяемого в текущей процедуре TaskText или CommentText.</p><p>В команде начала режима выравнивания по столбцам символы r, l, c определяют <emphasis>способ выравнивания</emphasis> в каждом столбце текста (r -- выравнивание по правому краю, l -- выравнивание по левому краю, c -- выравнивание по центру). Их количество должно быть равно числу столбцов. В каждом столбце должен быть хотя бы один непробельный символ (для пустого столбца достаточно указать малый неразрывный пробел \,).</p><p>Ввиду сложности управляющих последовательностей, связанных с выравниванием по столбцам, приведем пример их использования (пример взят из задания If26):</p><empty-line/><p><code>TaskText('Для данного вещественного~{x} найти значение ' +</code></p><p><code>'следующей функции~{f},', 0, 1);</code></p><p><code>TaskText('принимающей вещественные значения:', 0, 2);</code></p><p><code>TaskText('\[\Jrcrl&\,&\,& \-{x},& если {x}~\l~0,',</code></p><p><code>26, 3);</code></p><p><code>TaskText('&{f}({x})&~=~&{x}^2,& если 0~<~{x}~<~2,', 26, 4);</code></p><p><code>TaskText('&\,&\,& 4,& если {x}~\g~2.\j\]', 26, 5);</code></p><empty-line/><p>В результате обработки данной формулировки задания в окне задачника будет выведен текст:</p><p><image l:href="#pt4make17.png"/></p><p>В html-описании этот же текст будет отформатирован следующим образом:</p><p><image l:href="#pt4make18.png"/></p><p>Команду разрыва строки \| можно использовать как в обычном тексте, так и в режиме выделения с отступом или центрированием (в режиме выравнивания по столбцам переход на новую строку выполняется автоматически). Для более наглядного отображения в html-документе текста, выровненного по столбцам, данный текст рекомендуется дополнительно центрировать (как в приведенном выше примере) или использовать для него режим выравнивания с отступом.</p><p>Следует обратить внимание еще на одну особенность формулировок, содержащих выравнивание по столбцам: строки с подобным выравниванием, как правило, не нужно центрировать в окне задачника. В приведенном примере в трех последних вызовах процедуры TaskText в качестве параметра X указывается не нулевое значение (означающее центрирование по горизонтали), а число 26 -- значение позиции по горизонтали, начиная с которой требуется вывести указанную строку.</p><p>Указанное обстоятельство может затруднить использование для подобных формулировок нового варианта процедуры TaskText, появившегося в конструкторе версии 4.11. В этом варианте указывается единственный строковый параметр, содержащий все строки формулировки, разделенные символами разрыва строки (#13 или #10). При разборе этого параметра из каждой строки формулировки удаляются начальные и конечные пробелы, причем строки формулировки, оказавшиеся в результате пустыми, игнорируются, а все непустые строки выводятся в окне задачника в режиме центрирования. Тем не менее, и этот вариант процедуры TaskText позволяет обеспечить особое выравнивание требуемых строк в окне задачника. Для этого надо добавить некоторое количество вспомогательных начальных или конечных пробелов к строке, требующей специального выравнивания, причем первый начальный или последний конечный пробел надо <emphasis>экранировать</emphasis> символом \, чтобы не допустить его автоматического удаления. Прочие пробелы (следующие за начальным пробелом или предшествующие конечному пробелу) экранировать необязательно. Приведем пример определения формулировки, в котором используется новый вариант процедуры TaskText (следует обратить внимание на конечные пробелы, указанные в последних трех строках; в остальном текст формулировки не отличается от приведенного выше):</p><empty-line/><p><code>TaskText('Для данного вещественного~{x} найти значение следующей функции~{f},'#13 +</code></p><p><code>'принимающей вещественные значения:'#13 +</code></p><p><code>'\[\Jrcrl&\,&\,& \-{x},& если {x}~\l~0, \ '#13 +</code></p><p><code>'&{f}({x})&~=~&{x}^2,& если 0~<~{x}~<~2, \ '#13 +</code></p><p><code>'&\,&\,& 4,& если {x}~\g~2.\j\] \ ');</code></p><empty-line/><p>Управляющая последовательность \P предназначена для разделения абзацев. В тексте, отображаемом в окне задачника, данная команда игнорируется (подобно прочим командам, связанным с разделением на абзацы). Для нее не предусмотрено парной завершающей команды, поскольку необходимые теги при переходе к новому абзацу добавляются в текст html-страницы автоматически. Пробелы после команды \P при генерации html-страницы игнорируются, однако они учитываются при отображении текста в окне задачника.</p><subtitle>Генерация специальных символов</subtitle><empty-line/><p>Используя две универсальные" управляющие последовательности \h и \H, можно включать в текст задания или преамбулы специальные символы, входящие во вторую половину кодовой таблицы для западноевропейских языков ANSI Latin-1 (команда \h) или содержащиеся в Windows-шрифте Symbol (команда \H). После имени каждой из этих команд следует указать двузначное шестнадцатеричное число, определяющее код требуемого символа; при этом шестнадцатеричные цифры A, B, C, D, E, F можно указывать в любом регистре. Если двухсимвольный текст после команд нельзя преобразовать в шестнадцатеричное число или число не является допустимым, то команды возвращают символ "?" (знак вопроса).</p><p>В случае команды \h (символы таблицы Latin-1) допустимыми считаются числа из диапазона 128-255, за исключением кодов неотображаемых символов, например, кода неразрывного пробела 160 (A0) или мягкого" переноса 173 (AD). Символы таблицы Ansi Latin-1 с кодами 128-159 имеют в кодировке Unicode другие значения кодов; при генерации html-описаний для этих символов используются их коды в таблице Unicode.</p><p>С помощью команды \H можно получить только <emphasis>часть</emphasis> символов, определенных в Windows-шрифте Symbol. Исключены символы, уже присутствующие в таблицах ASCII и ANSI Latin-1 (например, цифры и знаки препинания) или имеющие идентичное начертание с символами из этих таблиц (например, заглавные греческие буквы, совпадающие по начертанию с латинскими: A, B, E, H, X и т. д.). Кроме того, исключены символы с кодами 230-239 и 243-254, представляющие собой фрагменты больших скобок.</p><p>Следует заметить, что для части математических символов нельзя обеспечить их правильное отображение в каждом из трех наиболее популярных веб-браузеров (Microsoft Internet Explorer, Mozilla Firefox и Opera) без использования средств веб-программирования. В браузерах Internet Explorer и Firefox можно подключать шрифты Windows, в том числе шрифт Symbol, однако в Opera это сделать нельзя. С другой стороны, в Opera и Firefox для отображения всех стандартных математических символов достаточно указать их код в Unicode-кодировке, однако в стандартных Windows-шрифтах, используемых браузером Internet Explorer, часть символов с требуемыми кодами отсутствует. При реализации команды \H для вывода подобных символов в html-документе был выбран вариант, обеспечивающий их правильное отображение в браузере Internet Explorer (и Mozilla Firefox): для этого используется Windows-шрифт Symbol. Однако в браузере Opera (и других браузерах, не поддерживающих шрифты Windows) <emphasis>данные символы будут отображаться неправильно</emphasis>.</p><p><strong>Примечание</strong>. Для возможности использования Windows-шрифтов в браузере Mozilla Firefox следует установить режим Разрешить веб-сайтам использовать свои шрифты вместо установленных". Соответствующий флажок находится в окне "Шрифты", которое можно отобразить с помощью следующей последовательности действий: выполнить команду меню "Инструменты | Настройки...", в появившемся окне "Настройки" перейти на вкладку "Содержимое" и в разделе "Шрифты и цвета" нажать кнопку "Дополнительно...".</p><p>С некоторыми часто используемыми специальными символами связаны особые управляющие последовательности (см. таблицу управляющих последовательностей, раздел Символы"). Все подобные символы правильно отображаются во всех перечисленных выше браузерах.</p><p>Хотя символ пересечения (∩, код 8745) имеется в стандартных Windows-шрифтах, прочие символы, связанные с множествами (объединение, вложение, принадлежность и т. д.), в этих шрифтах отсутствуют. Для того чтобы все обозначения, связанные с множествами, выглядели в html-документе единообразно, для отображения символа пересечения (команда \Hc7) используется соответствующий символ из шрифта Symbol.</p><p>Ниже приводятся таблицы всех символов, которые можно получить с помощью универсальных команд \h и \H. Первая таблица содержит символы, генерируемые командой \h, а вторая -- символы, генерируемые командой \H. Команды из второй таблицы, связанные с теми символами, которые будут неверно отображаться в браузере Opera, выделены полужирным шрифтом.</p><p>Таблица 1. Символы, генерируемые командой \h</p><table><tr><td><p>\h80</p><p>€</p></td><td><p> </p></td><td><p>\h82</p><p>‚</p></td><td><p>\h83</p><p>ƒ</p></td><td><p>\h84</p><p>„</p></td><td><p>\h85</p><p>…</p></td><td><p>\h86</p><p>†</p></td><td><p>\h87</p><p>‡</p></td><td><p>\h88</p><p>ˆ</p></td><td><p>\h89</p><p>‰</p></td></tr><tr><td><p>\h8a</p><p>Š</p></td><td><p>\h8b</p><p>‹</p></td><td><p>\h8c</p><p>Œ</p></td><td><p> </p></td><td><p>\h8e</p><p>Ž</p></td><td><p> </p></td><td><p> </p></td><td><p>\h91</p><p>‘</p></td><td><p>\h92</p><p>’</p></td><td><p>\h93</p><p>“</p></td></tr><tr><td><p>\h94</p><p>”</p></td><td><p>\h95</p><p>•</p></td><td><p>\h96</p><p>–</p></td><td><p>\h97</p><p>—</p></td><td><p>\h98</p><p>˜</p></td><td><p>\h99</p><p>™</p></td><td><p>\h9a</p><p>š</p></td><td><p>\h9b</p><p>›</p></td><td><p>\h9c</p><p>œ</p></td><td><p> </p></td></tr><tr><td><p>\h9e</p><p>ž</p></td><td><p>\h9f</p><p>Ÿ</p></td><td><p> </p></td><td><p>\ha1</p><p>¡</p></td><td><p>\ha2</p><p>¢</p></td><td><p>\ha3</p><p>£</p></td><td><p>\ha4</p><p>¤</p></td><td><p>\ha5</p><p>¥</p></td><td><p>\ha6</p><p>¦</p></td><td><p>\ha7</p><p>§</p></td></tr><tr><td><p>\ha8</p><p>¨</p></td><td><p>\ha9</p><p>©</p></td><td><p>\haa</p><p>ª</p></td><td><p>\hab</p><p>«</p></td><td><p>\hac</p><p>¬</p></td><td><p> </p></td><td><p>\hae</p><p>®</p></td><td><p>\haf</p><p>¯</p></td><td><p>\hb0</p><p>°</p></td><td><p>\hb1</p><p>±</p></td></tr><tr><td><p>\hb2</p><p>²</p></td><td><p>\hb3</p><p>³</p></td><td><p>\hb4</p><p>´</p></td><td><p>\hb5</p><p>µ</p></td><td><p>\hb6</p><p>¶</p></td><td><p>\hb7</p><p>·</p></td><td><p>\hb8</p><p>¸</p></td><td><p>\hb9</p><p>¹</p></td><td><p>\hba</p><p>º</p></td><td><p>\hbb</p><p>"</p></td></tr><tr><td><p>\hbc</p><p>¼</p></td><td><p>\hbd</p><p>½</p></td><td><p>\hbe</p><p>¾</p></td><td><p>\hbf</p><p>¿</p></td><td><p>\hc0</p><p>À</p></td><td><p>\hc1</p><p>Á</p></td><td><p>\hc2</p><p>Â</p></td><td><p>\hc3</p><p>Ã</p></td><td><p>\hc4</p><p>Ä</p></td><td><p>\hc5</p><p>Å</p></td></tr><tr><td><p>\hc6</p><p>Æ</p></td><td><p>\hc7</p><p>Ç</p></td><td><p>\hc8</p><p>È</p></td><td><p>\hc9</p><p>É</p></td><td><p>\hca</p><p>Ê</p></td><td><p>\hcb</p><p>Ë</p></td><td><p>\hcc</p><p>Ì</p></td><td><p>\hcd</p><p>Í</p></td><td><p>\hce</p><p>Î</p></td><td><p>\hcf</p><p>Ï</p></td></tr><tr><td><p>\hd0</p><p>Ð</p></td><td><p>\hd1</p><p>Ñ</p></td><td><p>\hd2</p><p>Ò</p></td><td><p>\hd3</p><p>Ó</p></td><td><p>\hd4</p><p>Ô</p></td><td><p>\hd5</p><p>Õ</p></td><td><p>\hd6</p><p>Ö</p></td><td><p>\hd7</p><p>×</p></td><td><p>\hd8</p><p>Ø</p></td><td><p>\hd9</p><p>Ù</p></td></tr><tr><td><p>\hda</p><p>Ú</p></td><td><p>\hdb</p><p>Û</p></td><td><p>\hdc</p><p>Ü</p></td><td><p>\hdd</p><p>Ý</p></td><td><p>\hde</p><p>Þ</p></td><td><p>\hdf</p><p>ß</p></td><td><p>\he0</p><p>à</p></td><td><p>\he1</p><p>á</p></td><td><p>\he2</p><p>â</p></td><td><p>\he3</p><p>ã</p></td></tr><tr><td><p>\he4</p><p>ä</p></td><td><p>\he5</p><p>å</p></td><td><p>\he6</p><p>æ</p></td><td><p>\he7</p><p>ç</p></td><td><p>\he8</p><p>è</p></td><td><p>\he9</p><p>é</p></td><td><p>\hea</p><p>ê</p></td><td><p>\heb</p><p>ë</p></td><td><p>\hec</p><p>ì</p></td><td><p>\hed</p><p>í</p></td></tr><tr><td><p>\hee</p><p>î</p></td><td><p>\hef</p><p>ï</p></td><td><p>\hf0</p><p>ð</p></td><td><p>\hf1</p><p>ñ</p></td><td><p>\hf2</p><p>ò</p></td><td><p>\hf3</p><p>ó</p></td><td><p>\hf4</p><p>ô</p></td><td><p>\hf5</p><p>õ</p></td><td><p>\hf6</p><p>ö</p></td><td><p>\hf7</p><p>÷</p></td></tr><tr><td><p>\hf8</p><p>ø</p></td><td><p>\hf9</p><p>ù</p></td><td><p>\hfa</p><p>ú</p></td><td><p>\hfb</p><p>û</p></td><td><p>\hfc</p><p>ü</p></td><td><p>\hfd</p><p>ý</p></td><td><p>\hfe</p><p>þ</p></td><td><p>\hff</p><p>ÿ</p></td><td><p> </p></td><td><p> </p></td></tr></table><empty-line/><p>Таблица 2. Символы, генерируемые командой \H</p><table><tr><td><p><strong>\H22</strong></p><p>"</p></td><td><p><strong>\H24</strong></p><p>$</p></td><td><p><strong>\H27</strong></p><p>'</p></td><td><p>\H2d</p><p>−</p></td><td><p><strong>\H40</strong></p><p>@</p></td><td><p>\H44</p><p>Δ</p></td><td><p>\H46</p><p>Φ</p></td><td><p>\H47</p><p>Γ</p></td><td><p>\H4c</p><p>Λ</p></td><td><p>\H50</p><p>Θ</p></td></tr><tr><td><p>\H51</p><p>Θ</p></td><td><p>\H53</p><p>Σ</p></td><td><p>\H56</p><p>ς</p></td><td><p>\H57</p><p>Ω</p></td><td><p>\H58</p><p>Ξ</p></td><td><p>\H59</p><p>Ψ</p></td><td><p><strong>\H5c</strong></p><p>\</p></td><td><p><strong>\H5e</strong></p><p>^</p></td><td><p>\H61</p><p>α</p></td><td><p>\H62</p><p>β</p></td></tr><tr><td><p>\H63</p><p>χ</p></td><td><p>\H64</p><p>δ</p></td><td><p>\H65</p><p>ε</p></td><td><p><strong>\H66</strong></p><p>f</p></td><td><p>\H67</p><p>γ</p></td><td><p>\H68</p><p>η</p></td><td><p>\H69</p><p>ι</p></td><td><p>\H6a</p><p>φ</p></td><td><p>\H6b</p><p>κ</p></td><td><p>\H6c</p><p>λ</p></td></tr><tr><td><p>\H6d</p><p>μ</p></td><td><p>\H6e</p><p>ν</p></td><td><p>\H70</p><p>π</p></td><td><p>\H71</p><p>θ</p></td><td><p>\H72</p><p>ρ</p></td><td><p>\H73</p><p>σ</p></td><td><p>\H74</p><p>τ</p></td><td><p>\H75</p><p>υ</p></td><td><p><strong>\H76</strong></p><p>v</p></td><td><p>\H77</p><p>ω</p></td></tr><tr><td><p>\H78</p><p>ξ</p></td><td><p>\H79</p><p>ψ</p></td><td><p>\H7a</p><p>ζ</p></td><td><p><strong>\Ha1</strong></p><p>¡</p></td><td><p>\Ha2</p><p>′</p></td><td><p>\Ha3</p><p>≤</p></td><td><p>\Ha5</p><p>∞</p></td><td><p>\Ha7</p><p>♣</p></td><td><p>\Ha8</p><p>♦</p></td><td><p>\Ha9</p><p>♥</p></td></tr><tr><td><p>\Haa</p><p>♠</p></td><td><p>\Hab</p><p>↔</p></td><td><p>\Hac</p><p>←</p></td><td><p>\Had</p><p>↑</p></td><td><p>\Hae</p><p>→</p></td><td><p>\Haf</p><p>↓</p></td><td><p>\Hb2</p><p>″</p></td><td><p>\Hb3</p><p>≥</p></td><td><p><strong>\Hb5</strong></p><p>µ</p></td><td><p>\Hb6</p><p>∂</p></td></tr><tr><td><p>\Hb9</p><p>≠</p></td><td><p>\Hba</p><p>≡</p></td><td><p>\Hbb</p><p>≈</p></td><td><p>\Hbd</p><p>│</p></td><td><p>\Hbe</p><p>─</p></td><td><p><strong>\Hbf</strong></p><p>¿</p></td><td><p>\Hc0</p><p>א</p></td><td><p><strong>\Hc1</strong></p><p>Á</p></td><td><p><strong>\Hc2</strong></p><p>Â</p></td><td><p><strong>\Hc3</strong></p><p>Ã</p></td></tr><tr><td><p><strong>\Hc4</strong></p><p>Ä</p></td><td><p><strong>\Hc5</strong></p><p>Å</p></td><td><p><strong>\Hc6</strong></p><p>Æ</p></td><td><p><strong>\Hc7</strong></p><p>Ç</p></td><td><p><strong>\Hc8</strong></p><p>È</p></td><td><p><strong>\Hc9</strong></p><p>É</p></td><td><p><strong>\Hca</strong></p><p>Ê</p></td><td><p><strong>\Hcb</strong></p><p>Ë</p></td><td><p><strong>\Hcc</strong></p><p>Ì</p></td><td><p><strong>\Hcd</strong></p><p>Í</p></td></tr><tr><td><p><strong>\Hce</strong></p><p>Î</p></td><td><p><strong>\Hcf</strong></p><p>Ï</p></td><td><p><strong>\Hd0</strong></p><p>Ð</p></td><td><p><strong>\Hd1</strong></p><p>Ñ</p></td><td><p>\Hd5</p><p>∏</p></td><td><p>\Hd6</p><p>√</p></td><td><p><strong>\Hd9</strong></p><p>Ù</p></td><td><p><strong>\Hda</strong></p><p>Ú</p></td><td><p><strong>\Hdb</strong></p><p>Û</p></td><td><p><strong>\Hdc</strong></p><p>Ü</p></td></tr><tr><td><p><strong>\Hdd</strong></p><p>Ý</p></td><td><p><strong>\Hde</strong></p><p>Þ</p></td><td><p><strong>\Hdf</strong></p><p>ß</p></td><td><p>\He0</p><p>◊</p></td><td><p>\He1</p><p>‹</p></td><td><p>\He5</p><p>∑</p></td><td><p>\Hf1</p><p>›</p></td><td><p>\Hf2</p><p>∫</p></td><td><p> </p></td><td><p> </p></td></tr></table><empty-line/></section><section><title><p>Модуль PT4TaskMakerNET: примеры разработки учебных заданий</p></title><subtitle>Создание простейшей сводной группы</subtitle><empty-line/><p>Вначале опишем действия по созданию наиболее простого варианта группы заданий -- <emphasis>сводной группы</emphasis>, в которой не разрабатываются новые задания, а лишь производится перекомпоновка заданий из имеющихся групп.</p><p>Создадим группу заданий MakerDemo, в которую импортируем два первых задания из базовой группы Begin. Следуя правилам об именовании dll-файлов с группами заданий, дадим нашей библиотеке имя PT4MakerDemo.</p><p>Файл PT4MakerDemo.pas, содержащий сводную группу заданий, является кратким и имеет стандартную структуру:</p><empty-line/><p><code>library PT4MakerDemo;</code></p><empty-line/><p><code>uses PT4TaskMakerNET;</code></p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('Begin', num);</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p><code>procedure inittaskgroup;</code></p><p><code>begin</code></p><p><code>CreateGroup('MakerDemo', 'Примеры различных задач',</code></p><p><code>'М. Э. Абрамян, 2013', 'qwqfsdf13dfttd', 2, InitTask);</code></p><p><code>end;</code></p><empty-line/><p><code>procedure activate(S: string);</code></p><p><code>begin</code></p><p><code>ActivateNET(S);</code></p><p><code>end;</code></p><empty-line/><p><code>begin</code></p><p><code>end.</code></p><empty-line/><p>К библиотеке подключается модуль PT4TaskMakerNET, после чего в ней описывается основная процедура группы заданий InitTask, определяющая задание по его номеру. Поскольку мы не создавали своих заданий, в данной процедуре используется только стандартная процедура UseTask, позволяющая импортировать задания из имеющихся групп. В нашем случае импортируются задания с номерами 1 и 2 из группы Begin.</p><p>Затем описывается процедура инициализации данной группы заданий. Она должна иметь стандартное имя inittaskgroup (<emphasis><strong>набранное строчными, т. е. маленькими буквами</strong></emphasis>). В этой процедуре вызывается процедура CreateGroup, в которой задаются настройки создаваемой группы: имя ('MakerDemo'), описание ('Примеры различных задач'), сведения об авторе, строковый ключ, число заданий (2) и основная процедура группы (InitTask).</p><p>После процедуры inittaskgroup описывается вспомогательная процедура activate (ее имя также должно быть набрано строчными буквами), в которой необходимо вызвать процедуру ActivateNET, описанную в модуле PT4TaskMakerNET.</p><subtitle>Тестирование созданной группы</subtitle><empty-line/><p>Для успешной компиляции программы с созданной группой необходимо, чтобы ей был доступен модуль PT4TaskMakerNET. Этот модуль входит в число стандартных модулей библиотеки системы PascalABC.NET и размещается в подкаталоге LIB системного каталога PascalABC.NET, поэтому копировать его в рабочий каталог не требуется. Однако даже при успешной компиляции программы просмотреть задания группы не удастся, так как созданную библиотеку (dll-файл) нельзя запускать на выполнение (при успешной компиляции будет выведено сообщение Невозможно запустить динамическую библиотеку".</p><p>Для тестирования полученной библиотеки необходимо создать вспомогательную программу, являющуюся заготовкой для выполнения заданий из созданной группы. Так как после успешной компиляции библиотеки в рабочем каталоге уже содержится файл PT4MakerDemo.dll, для создания программы-заготовки можно использовать программный модуль PT4Load. Вызвав его окно на экран (для этого достаточно использовать клавиатурную комбинацию [Shift]+[Ctrl]+[L]) и удалив, при необходимости, имя ранее введенного задания, мы должны увидеть в списке доступных групп заданий созданную нами группу MakerDemo. Если имя группы MakerDemo не отображается, значит, задачник не смог успешно загрузить эту группу из библиотеки PT4MakerDemo.dll. В этом случае необходимо проверить имя созданной библиотеки (в частности, наличие в нем префикса PT4) и наличие в файле библиотеки процедур inittaskgroup и activate, определенных по описанным выше правилам.</p><p>Если имя группы появилось в списке, то надо ввести в поле Задание" имя "MakerDemo1" и нажать клавишу [Enter] (или кнопку "Загрузка"); в результате будет создан файл MakerDemo1.pas, который сразу загрузится в редактор среды PascalABC.NET. Приведем содержимое этого файла:</p><empty-line/><empty-line/><p><code>uses PT4;</code></p><empty-line/><p><code>begin</code></p><p><code>Task('MakerDemo1');</code></p><empty-line/><p><code>end.</code></p><empty-line/><p>Поскольку мы собираемся просматривать задания группы в демо-режиме, добавим в конец строки с именем задания символ ?":</p><empty-line/><p><code>Task('MakerDemo1?');</code></p><empty-line/><p>После компиляции и запуска полученной программы на экране отобразится окно задачника с указанным заданием данной группы:</p><p><image l:href="#pt4make6.png"/></p><p>По умолчанию окно задачника отображается в режиме с динамической компоновкой, который появился в версии 4.11 и является более наглядным, чем режим с фиксированной компоновкой. Однако при разработке заданий желательно применять режим с фиксированной компоновкой, поскольку он позволит выявить недостатки форматирования (в частности, вертикального выравнивания данных), присущие только этому режиму. Для переключения между режимами отображения данных достаточно нажать клавишу [F4]. После выполнения этого действия окно задачника изменится следующим образом:</p><p><image l:href="#pt4make6a.png"/></p><p>В окне задачника можно просматривать все имеющиеся задания данной группы (нажимая клавиши [Enter] и [Backspace], а также генерировать различные варианты исходных данных и связанных с ними контрольных (т. е. правильных") результатов. При закрытии окна программа немедленно завершит работу, и мы вернемся в редактор среды PascalABC.NET. Заметим, что при последующих запусках программы будет автоматически выбираться тот режим окна задачника, в котором оно находилось в момент его предшествующего закрытия.</p><empty-line/><p><strong>Примечание</strong>. После добавления в группу нового задания было бы желательно, чтобы при звпуске тестирующей программы на экране сразу отображались данные, связанные с последним добавленным заданием. Чтобы не приходилось каждый раз изменять номер задания в процедуре Task, можно удалить этот номер, указав символ ?" сразу после имени группы: Task('MakerDemo?'). В этом случае при запуске программы на экране будет отображаться <emphasis>последнее</emphasis> задание данной группы.</p><subtitle>Добавление описания группы и ее подгрупп</subtitle><empty-line/><p>По тексту, расположенному выше названия задания MakerDemo1 (см. приведенные выше рисунки), мы видим, что импортированные из группы Begin задания входят в подгруппу с заголовком Ввод и вывод данных, оператор присваивания". В сводной группе MakerDemo мы можем добавить комментарий (<emphasis>преамбулу</emphasis>) как к самой группе, так и к любой имеющейся в ней подгруппе. Кроме того, мы можем <emphasis>импортировать</emphasis> преамбулу любой имеющейся группы или подгруппы. Для иллюстрации этих возможностей добавим в процедуру inittaskgroup новые операторы (их надо указать после вызова процедуры CreateGroup):</p><empty-line/><p><code>CommentText('Данная группа демонстрирует различные возможности');</code></p><p><code>CommentText('\Iконструктора учебных заданий\i \MPT4TaskMaker\m.');</code></p><empty-line/><p><code>Subgroup('Ввод и вывод данных, оператор присваивания');</code></p><p><code>CommentText('В этой подгруппе содержатся задания, импортированные');</code></p><p><code>CommentText('из группы Begin.\PПриводимый ниже абзац преамбулы');</code></p><p><code>CommentText('также импортирован из данной группы.\P');</code></p><p><code>UseComment('Begin');</code></p><empty-line/><p>Два первых вызова процедуры CommentText определяют текст преамбулы для группы MakerDemo. Обратите внимание на <emphasis>управляющие последовательности</emphasis>: пара последовательностей \I и \i выделяет <emphasis>курсивный</emphasis> фрагмент, а пара \M и \m выделяет фрагмент, в которым используется моноширинный шрифт. Последующий вызов процедуры Subgroup устанавливает режим определения преамбулы для подгруппы с указанным именем. В тексте этой преамбулы, который, как и текст преамбулы группы, определяется с помощью процедуры CommentText, используется управляющая последовательность \P, обеспечивающая переход к новому абзацу.</p><p>Наконец, последняя процедура (UseComment) импортирует преамбулу группы Begin в преамбулу нашей подгруппы Ввод и вывод данных, оператор присваивания". Имеется также вариант процедуры UseComment, позволяющий импортировать преамбулу подгруппы; в этом варианте следует указать два параметра: имя группы и заголовок требуемой подгруппы, входящей в эту группу. Импортировать преамбулы подгрупп можно только для тех групп заданий, в которых имеется разделение на подгруппы (обычно это группы, содержащие большое количество заданий). В группе Begin деления на подгруппы нет, поэтому из нее можно импортировать только преамбулу самой группы.</p><p>Для того чтобы ознакомиться с результатом сделанных изменений, следует сгенерировать html-страницу с текстом группы MakerDemo. Для этого достаточно внести небольшое изменение в тестирующую программу, а именно, следует заменить символ ?" в параметре процедуры Task на "#": Task('MakerDemo?'). Теперь при запуске данной программы на экране вместо окна задачника появится html-браузер с описанием созданной группы:</p><p><image l:href="#pt4make7.png"/></p><p>Обратите внимание на последний абзац в описании подгруппы (Все входные и выходные данные в заданиях этой группы являются вещественными числами"), который был импортирован из группы Begin.</p><p><strong>Примечание</strong>. Если указать в параметре процедуры Task символ #", не удаляя номер задания (например, Task('MakerDemo2#')), то в html-описание будет включено только задание с указанным номером. При этом будут также выведены комментарии ко всей группе и к той подгруппе, к которой относится выбранное задание. Для включения в html-страницу нескольких заданий (или групп заданий) достаточно для каждого из них вызвать процедуру Task с параметром, оканчивающимся символом "#".</p><subtitle>Добавление нового задания</subtitle><empty-line/><p>Добавим к нашей группе новое задание. Фактически это задание будет дублировать задание Begin3, однако вместо импортирования этого задания мы разработаем его самостоятельно. Все действия по созданию нового задания удобно реализовать во вспомогательной процедуре, которую можно назвать MakerDemo3 (таким образом, название процедуры будет соответствовать имени создаваемого задания, хотя это и не является обязательным):</p><empty-line/><p><code>procedure MakerDemo3;</code></p><p><code>var</code></p><p><code>a, b: real;</code></p><p><code>begin</code></p><p><code>CreateTask('Ввод и вывод данных, оператор присваивания');</code></p><p><code>TaskText('Даны стороны прямоугольника~{a} и~{b}.', 0, 2);</code></p><p><code>TaskText('Найти его площадь {S}~=~{a}\*{b} и периметр {P}~=~2\*({a}\;+\;{b}).',</code></p><p><code>0, 4);</code></p><p><code>a := RandomN(1, 99) / 10;</code></p><p><code>b := RandomN(1, 99) / 10;</code></p><p><code>DataR('a = ', a, xLeft, 3, 4);</code></p><p><code>DataR('b = ', b, xRight, 3, 4);</code></p><p><code>ResultR('S = ', a * b, 0, 2, 4);</code></p><p><code>ResultR('P = ', 2 * (a + b), 0, 4, 4);</code></p><p><code>SetTestCount(3);</code></p><p><code>end;</code></p><empty-line/><p>Описание процедуры MakerDemo3 (как и описания всех других процедур, обеспечивающих формирование новых заданий) следует разместить перед описанием процедуры InitTask.</p><p>Процедура MakerDemo3 включает все основные действия, используемые при формировании нового задания:</p><p><emphasis>инициализацию</emphasis> нового задания (процедура CreateTask; мы указали в этой процедуре, что данное задание должно входить в подгруппу Ввод и вывод данных, оператор присваивания", т. е. в ту же подгруппу, что и два предыдущих задания); определение его <emphasis>формулировки</emphasis> (процедуры TaskText; обратите внимание на используемые в этих процедурах управляющие последовательности); определение исходных (процедуры DataR) и результирующих данных (процедуры ResultR); при этом исходные данные генерируются с помощью датчика случайных чисел (процедура RandomN); указание количества успешных тестовых запусков программы учащегося, достаточных для регистрации задания как выполненного (процедура SetTestCount; для нашего простого задания достаточно трех <emphasis>проведенных подряд</emphasis> успешных тестовых запусков). Необходимо также включить вызов созданной процедуры в основную процедуру группы MakerDemo, связав его с номером 3:</p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('Begin', num);</code></p><p><code>3: MakerDemo3;</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p>Наконец, следует откорректировать число заданий в вызове процедуры CreateGroup, изменив его на 3.</p><p>Запустив тестирующую программу, мы увидим в html-описании группы MakerDemo формулировки трех заданий, а выполнив обратную замену в этой программе символа #" на "?" (в результате вызов процедуры Task опять примет вид Task('MakerDemo?')) и повторно запустив программу на выполнение, мы увидим окно задачника с загруженным заданием MakerDemo3. Заметим, что при последующих запусках проекта мы будем получать в окне задачника различные исходные данные; это связано с тем, что при генерации исходных данных используется датчик случайных чисел.</p><subtitle>Добавление заданий на обработку двумерных массивов и символьных строк</subtitle><empty-line/><p>Добавим к группе MakerDemo еще два задания: первое из них дублирует задание Matrix7 (подгруппа Двумерные массивы (матрицы): вывод элементов"), а второе не имеет полного аналога в группе String, однако может быть отнесено к ее первой подгруппе: "Символы и строки: основные операции". Реализуем эти задания в процедурах MakerDemo4 и MakerDemo5:</p><empty-line/><p><code>procedure MakerDemo4;</code></p><p><code>var</code></p><p><code>m, n, i, j, k: integer;</code></p><p><code>a: array [1..5, 1..8] of real;</code></p><p><code>begin</code></p><p><code>CreateTask('Двумерные массивы (матрицы): вывод элементов');</code></p><p><code>TaskText('Дана матрица размера~{M}\;\x\;{N} и целое число~{K} (1~\l~{K}~\l~{M}).',</code></p><p><code>0, 2);</code></p><p><code>TaskText('Вывести элементы {K}-й строки данной матрицы.', 0, 4);</code></p><p><code>m := RandomN(2, 5);</code></p><p><code>n := RandomN(4, 8);</code></p><p><code>k := 1;</code></p><p><code>if m = 5 then k := 0;</code></p><p><code>DataN('M = ', m, 3, 1, 1);</code></p><p><code>DataN('N = ', n, 10, 1, 1);</code></p><p><code>for i := 1 to m do</code></p><p><code>for j := 1 to n do</code></p><p><code>begin</code></p><p><code>a[i, j] := RandomR(-9.99, 9.99);</code></p><p><code>DataR(a[i,j], Center(j, n, 5, 1), i + k, 5);</code></p><p><code>end;</code></p><p><code>k := RandomN(1, m);</code></p><p><code>DataN('K = ', k, 68, 5, 1);</code></p><p><code>for j := 1 to n do</code></p><p><code>ResultR(a[k, j], Center(j, n, 5, 1), 3, 5);</code></p><p><code>SetTestCount(5);</code></p><p><code>end;</code></p><empty-line/><p><code>procedure MakerDemo5;</code></p><p><code>var</code></p><p><code>s: string;</code></p><p><code>begin</code></p><p><code>CreateTask('Символы и строки: основные операции');</code></p><p><code>TaskText('Дана непустая строка~{S}.', 0, 2);</code></p><p><code>TaskText('Вывести ее первый и последний символ.', 0, 4);</code></p><p><code>s := WordSample(RandomN(0, WordCount-1));</code></p><p><code>if CurrentTest = 3 then</code></p><p><code>while s[1] = s[Length(s)] do</code></p><p><code>s := WordSample(RandomN(0, WordCount-1));</code></p><p><code>DataS('S = ', s, 0, 3);</code></p><p><code>ResultC('Первый символ: ', s[1], xLeft, 3);</code></p><p><code>ResultC('Последний символ: ', s[Length(s)], xRight, 3);</code></p><p><code>SetTestCount(4);</code></p><p><code>end;</code></p><empty-line/><p>Обратите внимание на использование вспомогательной функции Center для центрирования строк матрицы в области исходных и результирующих данных: каждый элемент матрицы занимает 5 экранных позиций, а между элементами размещается по одному пробелу. В процедуре MakerDemo4 не вызывается процедура SetTestCount; в этом случае число успешных тестов, необходимых для регистрации задания как выполненного, по умолчанию полагается равным 5.</p><p>При выводе элементов исходной матрицы и результирующей матричной строки дополнительные комментарии указывать не требуется, поэтому используется вариант процедур DataR и ResultR, в котором комментарий отсутствует (этот вариант процедур групп Data и Result добавлен в версию 4.11 конструктора учебных заданий).</p><p>В процедуре MakerDemo5 для получения исходных символьных строк используются функции WordCount и WordSample. С помощью этих функций можно получать различные варианты русских слов. Заметим, что в конструкторе PT4TaskMaker имеются также функции EnWordCount и EnWordSample, с помощью которых можно получать варианты английских слов.</p><p>В процедуре MakerDemo5 использована еще одна возможность, появившаяся в версии 4.11 конструктора: функция CurrentTest, возвращающая порядковый номер текущего тестового запуска. Использование этой функции позволяет связать какой-либо особый вариант теста с некоторым номером тестового испытания, и тем самым гарантировать, что программа с решением задачи обязательно будет проверена на этом особом варианте теста. В нашем случае строка S выбирается из набора слов-образцов, среди которых имеется сравнительно большое число слов, начинающихся и оканчивающихся одной и той же буквой. Для более надежного тестирования решения желательно гарантировать, что в наборе тестов будет <emphasis>хотя бы один тест</emphasis>, в котором начальный и конечный символ исходной строки различаются. Разумеется, можно было бы <emphasis>всегда</emphasis> выбирать подобные строки, используя соответствующий цикл while. Однако при наличии функции CurrentTest в этом нет необходимости: достаточно выполнять подобный цикл для единственного теста, например, с номером 3, как это сделано в приведенной реализации задания. В дальнейшем мы рассмотрим более содержательный пример использования функции CurrentTest.</p><p>Осталось изменить количество заданий в вызове процедуры CreateGroup на 5 и включить вызовы новых процедур в основную процедуру группы InitTask:</p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('Begin', num);</code></p><p><code>3: MakerDemo3;</code></p><p><code>4: MakerDemo4;</code></p><p><code>5: MakerDemo5;</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p>Приведем вид окна задачника для новых заданий:</p><p><image l:href="#pt4make8.png"/></p><p><image l:href="#pt4make9.png"/></p><subtitle>Добавление заданий на обработку файлов</subtitle><empty-line/><p>Добавим к группе MakerDemo еще два задания: первое из них дублирует задание File63 (подгруппа Символьные и строковые файлы"), а второе -- задание Text16 (подгруппа "Текстовые файлы: основные операции"). Реализуем эти задания в процедурах MakerDemo6 и MakerDemo7:</p><empty-line/><p><code>function FileName(Len: integer): string;</code></p><p><code>const</code></p><p><code>c = '0123456789abcdefghijklmnopqrstuvwxyz';</code></p><p><code>var</code></p><p><code>i: integer;</code></p><p><code>begin</code></p><p><code>result := '';</code></p><p><code>for i := 1 to Len do</code></p><p><code>result := result + c[RandomN(1, Length(c))];</code></p><p><code>end;</code></p><empty-line/><p><code>procedure MakerDemo6;</code></p><p><code>var</code></p><p><code>k, i, j, jmax: integer;</code></p><p><code>s1, s2, s3: string;</code></p><p><code>fs1: file of ShortString;</code></p><p><code>fs2: file of ShortString;</code></p><p><code>fc3: file of char;</code></p><p><code>s: ShortString;</code></p><p><code>c: char;</code></p><p><code>begin</code></p><p><code>CreateTask('Символьные и строковые файлы');</code></p><p><code>TaskText(</code></p><p><code>'Дано целое число~{K} (\,0) и строковый файл.'#13 +</code></p><p><code>'Создать два новых файла: строковый, содержащий первые {K}~символов'#13 +</code></p><p><code>'каждой строки исходного файла, и символьный, содержащий {K}-й символ'#13 +</code></p><p><code>'каждой строки (если длина строки меньше~{K}, то в строковый файл'#13 +</code></p><p><code>'записывается вся строка, а в символьный файл записывается пробел).'</code></p><p><code>);</code></p><p><code>s1 := '1' + FileName(5) + '.tst';</code></p><p><code>s2 := '2' + FileName(5) + '.tst';</code></p><p><code>s3 := '3' + FileName(5) + '.tst';</code></p><p><code>Assign(fs1, s1);</code></p><p><code>Rewrite(fs1);</code></p><p><code>Assign(fs2, s2);</code></p><p><code>Rewrite(fs2);</code></p><p><code>Assign(fc3, s3);</code></p><p><code>Rewrite(fc3);</code></p><p><code>k := RandomN(2, 11);</code></p><p><code>jmax := 0;</code></p><p><code>for i := 1 to RandomN(10, 20) do</code></p><p><code>begin</code></p><p><code>j := RandomN(2, 16);</code></p><p><code>if jmax < j then</code></p><p><code>jmax := j;</code></p><p><code>s := FileName(j);</code></p><p><code>write(fs1, s);</code></p><p><code>if j >= k then</code></p><p><code>c := s[k]</code></p><p><code>else</code></p><p><code>c := ' ';</code></p><p><code>write(fc3, c);</code></p><p><code>s := copy(s, 1, k);</code></p><p><code>write(fs2,s);</code></p><p><code>end;</code></p><p><code>Close(fs1);</code></p><p><code>Close(fs2);</code></p><p><code>Close(fc3);</code></p><p><code>DataN('K = ', k, 0, 1, 1);</code></p><p><code>DataS('Имя исходного файла: ', s1, 3, 2);</code></p><p><code>DataS('Имя результирующего строкового файла: ', s2, 3, 4);</code></p><p><code>DataS('Имя результирующего символьного файла: ', s3, 3, 5);</code></p><p><code>DataComment('Содержимое исходного файла:', xRight, 2);</code></p><p><code>DataFileS(s1, 3, jmax + 3);</code></p><p><code>ResultComment('Содержимое результирующего строкового файла:',</code></p><p><code>0, 2);</code></p><p><code>ResultComment('Содержимое результирующего символьного файла:',</code></p><p><code>0, 4);</code></p><p><code>ResultFileS(s2, 3, k + 3);</code></p><p><code>ResultFileC(s3, 5, 4);</code></p><p><code>end;</code></p><empty-line/><p><code>procedure MakerDemo7;</code></p><p><code>var</code></p><p><code>p: integer;</code></p><p><code>s, s1, s2, s0: string;</code></p><p><code>t1, t2: text;</code></p><p><code>begin</code></p><p><code>CreateTask('Текстовые файлы: основные операции');</code></p><p><code>TaskText('Дан текстовый файл.', 0, 2);</code></p><p><code>TaskText('Удалить из него все пустые строки.', 0, 4);</code></p><p><code>s1 := FileName(6) + '.tst';</code></p><p><code>s2 := '#' + FileName(6) + '.tst';</code></p><p><code>s := TextSample(RandomN(0, TextCount-1));</code></p><p><code>Assign(t2, s2);</code></p><p><code>Rewrite(t2);</code></p><p><code>Assign(t1, s1);</code></p><p><code>Rewrite(t1);</code></p><p><code>writeln(t2, s);</code></p><p><code>Close(t2);</code></p><p><code>s0 := #13#10#13#10;</code></p><p><code>p := Pos(s0, s);</code></p><p><code>while p <> 0 do</code></p><p><code>begin</code></p><p><code>Delete(s, p, 2);</code></p><p><code>p := Pos(s0, s);</code></p><p><code>end;</code></p><p><code>writeln(t1, s);</code></p><p><code>Close(t1);</code></p><p><code>ResultFileT(s1, 1, 5);</code></p><p><code>Rename(t2, s1);</code></p><p><code>DataFileT(s1, 2, 5);</code></p><p><code>DataS('Имя файла: ', s1, 0, 1);</code></p><p><code>SetTestCount(3);</code></p><p><code>end;</code></p><empty-line/><p>При реализации этих заданий используется вспомогательная функция FileName(Len), позволяющая создать случайное имя файла длины Len (без расширения). Имя файла при этом будет содержать только цифры и строчные (маленькие) латинские буквы.</p><p>Имена файлов, полученные с помощью функции FileName, дополняются расширением .tst (заметим, что в базовых группах File, Text и Param это расширение используется в именах всех исходных и результирующих файлов).</p><p>Функция FileName используется также для генерации элементов строкового файла в процедуре MakerDemo6.</p><p>Для того чтобы предотвратить возможность случайного совпадения имен файлов, в процедуре MakerDemo6 к созданным именам добавляются префиксы: 1 для первого файла, 2 для второго, 3 для третьего. В процедуре MakerDemo7 имя временного файла дополняется префиксом #, что также гарантирует его отличие от имени основного файла задания.</p><p>В процедуре MakerDemo6 использован новый вариант процедуры TaskText, появившийся в версии 4.11 задачника. В этом варианте процедура TaskText принимает один строковый параметр, который определяет <emphasis>всю формулировку задания</emphasis>, причем в качестве разделителей строк, входящих в формулировку, можно использовать символы #13, #10 или их комбинацию #13#10 (в указанном порядке). Новый вариант процедуры TaskText позволяет более наглядно отобразить формулировку задания и не требует указания дополнительных параметров.</p><p>При реализации задания на обработку текстовых файлов для генерации содержимого файла используются функции TextCount и TextSample. Строка, возвращаемая функцией TextSample, представляет собой текст, содержащий <emphasis>маркеры конца строки</emphasis> -- символы #13#10. Указанные символы <emphasis>разделяют соседние строки текста</emphasis> (в конце текста маркер конца строки не указывается). Благодаря наличию маркеров конца строки полученный текст можно записать в текстовый файл с помощью единственной процедуры writeln, которая, кроме записи текста, обеспечивает добавление маркера конца строки в конец файла.</p><p>После разработки новых заданий необходимо изменить количество заданий в вызове процедуры CreateGroup на 7 и включить вызовы новых процедур в основную процедуру группы InitTask:</p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('Begin', num);</code></p><p><code>3: MakerDemo3;</code></p><p><code>4: MakerDemo4;</code></p><p><code>5: MakerDemo5;</code></p><p><code>6: MakerDemo6;</code></p><p><code>7: MakerDemo7;</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p>Приведем вид окна задачника для новых заданий:</p><p><image l:href="#pt4make10.png"/></p><p><image l:href="#pt4make11.png"/></p><subtitle>Добавление заданий на обработку динамических структур данных</subtitle><empty-line/><p>Наконец, добавим в нашу группу задание, посвященное обработке динамических структур данных, причем представим его в двух вариантах: традиционном, основанном на использовании записей типа TNode и связанных с ними указателей типа PNode, и объектном", характерном для .NET-языков (C#, Visual Basic .NET, PascalABC.NET), а также языков Python и Java. Следует подчеркнуть, что при <emphasis>разработке</emphasis> как традиционного, так и объектного варианта заданий на динамические структуры надо использовать типы TNode и PNode и связанные с ними процедуры конструктора учебных заданий. В то же время, при <emphasis>выполнении</emphasis> объектного варианта задания на соответствующем языке требуется использовать объекты типа Node (которые при разработке задания не применяются).</p><p>Задание, которое мы реализуем, дублирует задание Dynamic30, посвященное преобразованию односвязного списка в двусвязный (подгруппа Динамические структуры данных: двусвязный список"). Оформим два варианта этого задания в виде процедур MakerDemo8 и MakerDemo8Obj:</p><empty-line/><p><code>var WrongNode: TNode;</code></p><empty-line/><p><code>procedure MakerDemo8Data;</code></p><p><code>var</code></p><p><code>i, n: integer;</code></p><p><code>p, p1, p2: PNode;</code></p><p><code>begin</code></p><p><code>if RandomN(1, 4) = 1 then</code></p><p><code>n := 1</code></p><p><code>else</code></p><p><code>n := RandomN(2, 9);</code></p><p><code>case CurrentTest of</code></p><p><code>2: n := 1;</code></p><p><code>4: n := RandomN(2, 9);</code></p><p><code>end;</code></p><p><code>new(p1);</code></p><p><code>p1^.Data := RandomN(10, 99);</code></p><p><code>p1^.Prev := nil;</code></p><p><code>p2 := p1;</code></p><p><code>for i := 2 to n do</code></p><p><code>begin</code></p><p><code>new(p);</code></p><p><code>p^.Data := RandomN(10, 99);</code></p><p><code>p^.Prev := p2;</code></p><p><code>p2^.Next := p;</code></p><p><code>p2 := p;</code></p><p><code>end;</code></p><p><code>p2^.Next := nil;</code></p><p><code>SetPointer(1, p1);</code></p><p><code>SetPointer(2, p2);</code></p><p><code>ResultP('Последний элемент: ', 2, 0, 2);</code></p><p><code>ResultList(1, 0, 3);</code></p><p><code>ShowPointer(2);</code></p><p><code>DataP(1, 0, 2);</code></p><p><code>p := p1;</code></p><p><code>for i := 1 to n do</code></p><p><code>begin</code></p><p><code>p^.Prev := @WrongNode;</code></p><p><code>p := p^.Next;</code></p><p><code>end;</code></p><p><code>DataList(1, 0, 3);</code></p><p><code>ShowPointer(1);</code></p><p><code>end;</code></p><empty-line/><p><code>procedure MakerDemo8;</code></p><p><code>begin</code></p><p><code>CreateTask('Динамические структуры данных: двусвязный список');</code></p><p><code>TaskText('Дан указатель~{P}_1 на начало непустой цепочки ' +</code></p><p><code>'элементов-записей типа TNode,', 0, 1);</code></p><p><code>TaskText('связанных между собой с помощью поля Next. Используя ' +</code></p><p><code>'поле Prev записи TNode,', 0, 2);</code></p><p><code>TaskText('преобразовать исходную (\Iодносвязную\i) цепочку ' +</code></p><p><code>'в \Iдвусвязную\i, в которой каждый', 0, 3);</code></p><p><code>TaskText('элемент связан не только с последующим элементом ' +</code></p><p><code>'(с помощью поля Next),', 0, 4);</code></p><p><code>TaskText('но и с предыдущим (с помощью поля Prev). Поле Prev ' +</code></p><p><code>'первого элемента положить', 0, 5);</code></p><p><code>TaskText('равным \N. Вывести указатель на последний элемент ' +</code></p><p><code>'преобразованной цепочки.', 0, 0);</code></p><p><code>MakerDemo8Data;</code></p><p><code>end;</code></p><empty-line/><p><code>procedure MakerDemo8Obj;</code></p><p><code>begin</code></p><p><code>CreateTask('Динамические структуры данных: двусвязный список');</code></p><p><code>TaskText(</code></p><p><code>'Дана ссылка~{A}_1 на начало непустой цепочки элементов-объектов типа Node,'#13 +</code></p><p><code>'связанных между собой с помощью своих свойств Next. Используя свойства Prev'#13 +</code></p><p><code>'данных объектов, преобразовать исходную (\Iодносвязную\i) цепочку в \Iдвусвязную\i,'#13 +</code></p><p><code>'в которой каждый элемент связан не только с последующим элементом (с помощью'#13 +</code></p><p><code>'свойства Next), но и с предыдущим (с помощью свойства Prev). Свойство Prev'#13 +</code></p><p><code>'первого элемента положить равным \O. Вывести ссылку~{A}_2 на последний'#13 +</code></p><p><code>'элемент преобразованной цепочки.'</code></p><p><code>);</code></p><p><code>SetObjectStyle;</code></p><p><code>MakerDemo8Data;</code></p><p><code>end;</code></p><empty-line/><p>Анализируя приведенные варианты процедур, легко заметить, что они отличаются лишь деталями формулировки задания. Алгоритмы генерации исходных и контрольных данных для традиционного и объектного вариантов совпадают, поэтому они выделены в отдельную вспомогательную процедуру MakerDemo8Data. В то же время <emphasis>представления</emphasis> динамических структур и связанных с ними указателей или объектов будут отличаться (см. рисунки, приведенные ниже). Необходимые корректировки в представлении динамических структур выполняются задачником автоматически, с учетом используемого языка программирования.</p><p>Однако для языка PascalABC.NET требуемую настройку необходимо выполнить явно, так как в нем можно использовать оба варианта представления динамических структур: традиционный (как для обычного Паскаля в системах Delphi и Free Pascal Lazarus) и объектный (как в языках C#, Visual Basic .NET, Python и Java). Для того чтобы представление динамических данных при выполнении задания в среде PascalABC.NET соответствовало объектному варианту, следует в начале процедуры, реализующей задание (перед вызовом любых процедур, связанных с указателями и динамическими структурами), вызвать специальную процедуру без параметров SetObjectStyle. Для остальных языков данная процедура не выполняет никаких действий.</p><p>Обратите внимание на возможность использования в формулировке задания более 5 экранных строк. Строки, которые не умещаются в области формулировки задания, следует добавлять к заданию процедурой TaskText, указывая в качестве последнего параметра процедуры число 0 (см. процедуру MakerDemo8). Еще проще задавать длинные" формулировки заданий с помощью нового варианта процедуры TaskText с единственным строковым параметром, содержащим все строки формулировки (см. процедуру MakerDemo9). При наличии подобных строк в окне задачника (если окно находится в режиме с фиксированной компоновкой) слева от области формулировки появятся кнопки, обеспечивающие прокрутку формулировки задания; кроме этих кнопок для прокрутки можно также использовать стандартные клавиши, в частности, клавиши со стрелками.</p><p>Для того чтобы имя нулевого указателя (или объекта) соответствовало используемому языку программирования, в формулировке задания применяются управляющие последовательности \N (имя нулевого указателя) и \O (имя нулевого объекта). Для языка PascalABC.NET обе эти последовательности генерируют текст nil.</p><p>Достаточно часто алгоритмы, разработанные учащимися для обработки динамических структур данных, дают неверные результаты в случае особых (хотя и допустимых) структур, например, состоящих только из одного элемента. Поэтому желательно предусмотреть появление подобных структур в тестовых наборах исходных данных. В наших заданиях исходный список, состоящий из <emphasis>одного</emphasis> элемента, будет предлагаться программе учащегося в среднем один раз при каждых четырех тестовых испытаниях. Кроме того, благодаря использованию функции CurrentTest, появившейся в версии 4.11 конструктора, вариант списка с единственным элементом будет предложен программе учащегося для обработки в тесте номер 2, а вариант списка с более чем одним элементом -- в тесте номер 4. Таким образом, можно гарантировать, что при прохождении набора из 5 тестовых испытаний программе будут предложены как стандартные", так и "особые" наборы исходных данных.</p><p>При формировании односвязной структуры неиспользуемые поля Prev для каждого элемента структуры следует положить равными адресу фиктивного" элемента (в нашем случае -- переменной WrongNode), не связанного с данной структурой. Заметим, что для всех элементов, кроме первого, значения поля Prev можно было бы положить равными nil, однако это не подходит для первого элемента: если поле Prev первого элемента будет равно nil, то слева от него будет выведен "лишний" (в данной ситуации) текст nil<.</p><p>Характерной особенностью разработки заданий на динамические структуры является <emphasis>обратный порядок</emphasis> создания этих структур: вначале создаются <emphasis>контрольные</emphasis> структуры (которые сразу передаются в задачник), а затем они преобразуются в соответствующие <emphasis>исходные</emphasis> структуры, которые должны не только передаваться в задачник, но и <emphasis>оставаться в памяти</emphasis>, чтобы в дальнейшем их можно было использовать в программе учащегося, выполняющей это задание.</p><p>Если в группу включаются задания на динамические структуры, то необходимо <emphasis>анализировать текущий язык программирования</emphasis>, используемый задачником. Это обусловлено двумя причинами:</p><p>имеются языки, для которых отсутствует возможность выполнять задания на обработку динамических структур (например, Visual Basic и 1C); в языках платформы .NET, а также Python и Java, необходимо использовать объектный" стиль формулировок вместо стиля, основанного на указателях и применяемого для языков Pascal и C++. Кроме того, следует определиться с выбором стиля для языка PascalABC.NET, поскольку в нем можно использовать как стиль указателей, так и стиль объектов. Можно, например, включить в группу заданий для языка PascalABC.NET оба варианта каждого задания.</p><p>Отмеченные обстоятельства приводят к тому, что для разных языков программирования создаваемая группа может содержать разное число заданий и, кроме того, для этих заданий будут использоваться разные инициализирующие процедуры.</p><p>С учетом этих замечаний изменим основную процедуру группы InitTask следующим образом:</p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('Begin', num);</code></p><p><code>3: MakerDemo3;</code></p><p><code>4: MakerDemo4;</code></p><p><code>5: MakerDemo5;</code></p><p><code>6: MakerDemo6;</code></p><p><code>7: MakerDemo7;</code></p><p><code>8: if CurrentLanguage and lgWithPointers <> 0 then</code></p><p><code>MakerDemo8</code></p><p><code>else</code></p><p><code>MakerDemo8Obj;</code></p><p><code>9: MakerDemo8Obj;</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p>В этой процедуре используется функция CurrentLanguage, позволяющая определить текущий язык программирования, используемый задачником. Если текущий язык относится к категории языков, поддерживающих указатели (в том числе PascalABC.NET), то в качестве задания номер 8 вызывается процедура MakerDemo8, в которой задание формулируется в терминах указателей. В противном случае вызывается вариант задания, использующий объектную терминологию. При использовании языка PascalABC.NET число заданий в группе будет равно 9; при этом дополнительное задание номер 9 будет представлять собой объектный" вариант задания номер 8.</p><p>Функцию CurrentLanguage потребуется использовать и в начале процедуры inittaskgroup для того, чтобы правильно определить количество заданий в группе для разных языков программирования. Приведем фрагмент, на который надо заменить вызов процедуры CreateGroup и предшествующее ему ключевое слово begin (обратите внимание на то, что теперь в качестве предпоследнего параметра процедуры CreateGroup используется переменная n):</p><empty-line/><p><code>var</code></p><p><code>n: integer;</code></p><p><code>begin</code></p><p><code>n := 7;</code></p><p><code>if CurrentLanguage = lgPascalABCNET then</code></p><p><code>n := 9</code></p><p><code>else</code></p><p><code>if CurrentLanguage and (lgWithPointers or lgWithObjects) <> 0 then</code></p><p><code>n := 8;</code></p><p><code>CreateGroup('MakerDemo', 'Примеры различных задач',</code></p><p><code>'М. Э. Абрамян, 2013', 'qwqfsdf13dfttd', n, InitTask);</code></p><empty-line/><p>Приведенный набор условий будет правильно определять количество заданий и в случае, если состав языков, поддерживаемых задачником, будет расширен. Это обеспечивается тем, что в условиях используются не константы для конкретных языков (за исключением константы lgPascalABCNET), а битовые маски lgWithPointers и lgWithObjects. Первая из этих масок включает все языки, для которых в задачнике можно использовать варианты заданий на динамические структуры, основанные на указателях, а вторая -- все языки, позволяющие использовать варианты аналогичных заданий в объектной терминологии.</p><p>В среде PascalABC.NET можно протестировать оба варианта реализованного задания. Приведем вид окна задачника для этого задания (первый рисунок соответствует варианту задания, использующему указатели, второй -- варианту, использующему объекты). Обратите внимание на кнопки, расположенные справа от формулировки задания и обеспечивающие ее прокрутку.</p><p><image l:href="#pt4make12.png"/></p><p><image l:href="#pt4make13.png"/></p><p>Завершая оформление модуля PT4MakerDemo, добавим комментарии к новым подгруппам заданий (указанные операторы следует поместить в конец процедуры inittaskgroup):</p><empty-line/><p><code>Subgroup('Двумерные массивы (матрицы): вывод элементов');</code></p><p><code>CommentText('Данное задание дублирует задание Matrix7.');</code></p><empty-line/><p><code>Subgroup('Символьные и строковые файлы');</code></p><p><code>CommentText('Данное задание дублирует задание File63.');</code></p><p><code>CommentText('Оно демонстрирует особенности, связанные с двоичными');</code></p><p><code>CommentText('\Iстроковыми\i файлами.');</code></p><empty-line/><p><code>Subgroup('Текстовые файлы: основные операции');</code></p><p><code>CommentText('Данное задание дублирует задание Text16.');</code></p><empty-line/><p><code>Subgroup('Динамические структуры данных: двусвязный список');</code></p><p><code>CommentText('Данное задание дублирует задание Dynamic30.');</code></p><p><code>CommentText('\PЗадание реализовано в двух вариантах: основанном на использовании указателей');</code></p><p><code>CommentText('(для языков Pascal и C++) и основанном на использовании объектов (для языков платформы .NET,');</code></p><p><code>CommentText('а также Python и Java). Для языка Visual Basic это задание недоступно.');</code></p><p><code>CommentText('В системе PascalABC.NET доступны оба варианта задания.');</code></p><empty-line/><p>Приведем заключительную часть html-страницы с описанием данной группы:</p><p><image l:href="#pt4make14.png"/></p></section><section><title><p>Модуль PT4TaskMakerNET: разработка заданий, связанных с ЕГЭ по информатике</p></title><subtitle>Группы заданий Exam и их особенности</subtitle><empty-line/><p>Начиная с версии 4.10, в базовый набор задачника Programming Taskbook для языков Pascal и C++ входят специальные группы заданий, связанные с ЕГЭ по информатике: ExamBegin и ExamTaskC. Порядок выполнения заданий из этих групп имеет ряд особенностей, основной из которых является отказ от применения специальных средств ввода-вывода, входящих в состав задачника. В заданиях групп Exam для ввода-вывода надо применять стандартные средства используемого языка программирования. Это позволяет максимально приблизить вид программы, выполняющей задание, к виду, требуемому на экзамене, а также учесть при выполнении задания его дополнительные особенности, связанные с организацией ввода исходных данных и форматированием результатов.</p><p>С использованием конструктора учебных заданий PT4TaskMaker преподаватель может разрабатывать новые группы заданий, связанные с ЕГЭ по информатике. При этом необходимо следовать дополнительным правилам, основные из которых приводятся ниже.</p><p>Любые группы заданий, связанные с ЕГЭ по информатике, должны иметь имена, начинающиеся с префикса Exam (для групп с этим префиксом задачник генерирует специальные программы-заготовки, позволяющие использовать при выполнении задания стандартные средства ввода-вывода). Необходимо проверять номер текущей версии задачника и текущий язык программирования, создавая новую группу тольков случае, если версия имеет номер не ниже 4.10, а языком программирования является Pascal или C++. В преамбуле к группе заданий желательно отметить тот факт, что для ввода-вывода необходимо использовать стандартные средства языка. В новые группы Exam следует импортировать только те задания, которые также относятся к группам Exam. Набор исходных и контрольных данных надо сохранять в текстовых файлах, передавая задачнику информацию об именах этих файлов (процедурами DataS) и связывая содержимое этих файлов с разделами исходных и результирующих данных (процедурами DataFileT и ResultFileT соответственно). При любых вариантах наборов исходных данных соответствующие контрольные файлы не должны быть пустыми (при наличии пустого файла результатов задачник считает запуск программы ознакомительным). Проиллюстрируем эти правила, разработав в среде PascalABC.NET демонстрационную группу заданий ExamDemo. Задания, связанные с ЕГЭ, вполне допустимо разрабатывать и на других языках, поддерживаемых конструктором учебных заданий, в частности, на языке C++ или на языке Pascal в средах Delphi или Lazarus, причем полученные реализации не будут иметь никаких существенных отличий от реализации, приведенной ниже. Задания можно разрабатывать даже на языке C#, несмотря на то что <emphasis>выполнять</emphasis> их на этом языке будет нельзя.</p><subtitle>Реализация сводной группы заданий</subtitle><empty-line/><p>Напомним, что <emphasis>сводной группой</emphasis> называется группа, все задания которой импортированы из уже имеющихся групп. Сводные группы оказываются очень полезными при составлении вариантов проверочных работ, поскольку позволяют дать заданиям новые имена и тем самым затрудняют применение разного рода шпаргалок.</p><p>Так как в сводных группах отсутствуют новые задания, при разработке сводных групп для заданий, связанных с ЕГЭ по информатике, достаточно учесть правила 1-4, приведенные в предыдущем пункте.</p><p>Будем предполагать, что общие правила разработки новых групп заданий в среде PascalABC.NET читателю известны (см. раздел Примеры").</p><p>Следуя правилам именования групп (имя должно состоять из латинских букв и цифр, иметь длину не более 9 символов и не оканчиваться цифрой), а также правилу 1 из предыдущего пункта (наличие префикса Exam), назовем нашу группу ExamDemo. Динамическая библиотека в этом случае должна иметь имя PT4ExamDemo.</p><p>Импортируем в группу ExamDemo несколько заданий из обеих групп Exam, входящих в базовый набор. Из группы ExamBegin возьмем задания ExamBegin71 и ExamBegin72, входящие в подгруппу "Преобразование массивов" и связанные перестановкой элементов массива. Из группы ExamTaskC возьмем серию из 12 заданий ExamTaskC25-ExamTaskC36, объединенных общей предметной областью: сведениями об абитуриентах из различных школ.</p><p>Учитывая правила подготовки динамических библиотек с группами учебных заданий, а также правила 2 и 3 из предыдущего пункта, получим следующий вариант нашей библиотеки (файл PT4ExamDemo.pas):</p><empty-line/><p><code>library PT4ExamDemo;</code></p><empty-line/><p><code>uses PT4TaskMakerNET;</code></p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('ExamBegin', 70 + num);</code></p><p><code>3..14: UseTask('ExamTaskC', 22 + num);</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p><code>procedure inittaskgroup;</code></p><p><code>begin</code></p><p><code>if (CurrentVersion < '4.10') or</code></p><p><code>(CurrentLanguage and (lgPascal or lgCPP) = 0) then</code></p><p><code>exit;</code></p><p><code>CreateGroup('ExamDemo', '^ЕГЭ по информатике: примеры различных задач',</code></p><p><code>'М. Э. Абрамян, 2013', 'qdfedsag33gbg45j', 14, InitTask);</code></p><p><code>CommentText('\PПри выполнении заданий данной группы вместо');</code></p><p><code>CommentText('специальных операций ввода-вывода, предоставляемых');</code></p><p><code>CommentText('задачником, необходимо применять стандартные операции');</code></p><p><code>CommentText('используемого языка программирования: процедуры');</code></p><p><code>CommentText('\MRead\m/\MReadln\m\:\MWrite\m/\MWriteln\m для языка');</code></p><p><code>CommentText('Pascal, потоки \Mcin\m\:\Mcout\m для языка C++.');</code></p><p><code>end;</code></p><empty-line/><p><code>procedure activate(S: string);</code></p><p><code>begin</code></p><p><code>ActivateNET(S);</code></p><p><code>end;</code></p><empty-line/><p><code>begin</code></p><p><code>end.</code></p><empty-line/><p>Кратко опишем полученную программу. Вначале к ней подключается модуль PT4TaskMakerNET, в котором реализован конструктор учебных заданий для среды PascalABC.NET. Затем следует описание основной процедуры группы заданий InitTask, определяющей задание по его номеру. Поскольку мы не создавали своих заданий, в данной процедуре используется только стандартная процедура UseTask, позволяющая импортировать задания из имеющихся групп. В нашем случае импортируются задания с номерами 71 и 72 из группы ExamBegin и задания с номерами 25-36 из группы ExamTaskC (всего 14 заданий).</p><p>Затем описывается процедура инициализации данной группы заданий. Она имеет стандартное имя inittaskgroup (набранное строчными, т. е. маленькими буквами). В этой процедуре вызывается процедура CreateGroup, в которой задаются характеристики создаваемой группы: имя ('ExamDemo'), описание ('^ЕГЭ по информатике: примеры различных задач'), сведения об авторе, строковый ключ, число заданий (14) и основная процедура группы (InitTask).</p><p>Поскольку надо гарантировать, что группа будет создана только в случае использования задачника версии не ниже 4.10 и только для языков Pascal и С++, перед вызовом процедуры инициализации группы CreateGroup выполняется проверка перечисленных выше условий. Если хотя бы одно из условий нарушено, то выполняется немедленный выход из процедуры inittaskgroup, и группа ExamDemo не создается.</p><p>Следует обратить внимание на наличие символа-метки ^" в начале строки-описания группы. Этот символ отменяет автоматическое преобразование к нижнему регистру первой буквы описания при его выводе в программных модулях PT4Demo и PT4Load (если бы символ "^" отсутствовал, то строка с описанием данной группы имела бы вид "Тема: еГЭ по информатике: примеры различных задач").</p><p>Наконец, в соответствии с правилом 3, мы включили в создаваемую группу <emphasis>преамбулу</emphasis>, в которой отмечается основная особенность данной группы: необходимость использования стандартных средств ввода-вывода (для определения текста преамбулы вызываются процедуры CommentText). Обратите внимание на управляющие последовательности \M-\m, позволяющие отобразить фрагмент текста моноширинным шрифтом.</p><p>После процедуры inittaskgroup описывается вспомогательная процедура activate (ее имя также должно быть набрано строчными буквами), в которой необходимо вызвать процедуру ActivateNET, описанную в модуле PT4TaskMakerNET.</p><p>Для успешной компиляции программы с созданной группой необходимо, чтобы ей был доступен модуль PT4TaskMakerNET. Этот модуль входит в число стандартных модулей библиотеки системы PascalABC.NET и размещается в подкаталоге LIB системного каталога PascalABC.NET, поэтому копировать его в рабочий каталог не требуется.</p><p>Для того чтобы при успешной компиляции можно было сразу просмотреть содержимое созданной группы, достаточно использовать вспомогательную тестирующую программу, являющуюся заготовкой для выполнения заданий из созданной группы. Эту программу проще всего создать с помощью программного модуля PT4Load, нажав комбинацию клавиш [Shift]+[Ctrl]+[L] и введя в поле Задание"; имя первого задания группы: ExamDemo1. В результате будет создан файл с именем ExamDemo1.pas со следующим содержимым:</p><empty-line/><p><code>uses PT4Exam;</code></p><empty-line/><p><code>begin</code></p><p><code>Task('ExamDemo1');</code></p><empty-line/><p><code>end.</code></p><empty-line/><p>Следует обратить внимание на то, что к созданной программе подключается не традиционный модуль задачника PT4, а специальный его вариант PT4Exam, используемый для заданий, связанных с ЕГЭ.</p><p>Для того чтобы тестирующая программа отображала задания в демонстрационном режиме, причем при ее запуске на экране отображалось последнее из заданий, включенных в группу, достаточно заменить в процедуре Task номер задания на символ ?": Task('ExamDemo?').</p><p>Теперь при запуске тестирующей программы на экране отобразится окно задачника с заданием ExamDemo14:</p><p><image l:href="#pt4exam1a.png"/></p><p>По умолчанию окно задачника отображается в режиме с динамической компоновкой, который появился в версии 4.11 и является более наглядным, чем режим с фиксированной компоновкой. Однако при разработке заданий желательно применять режим с фиксированной компоновкой, поскольку он позволит выявить недостатки форматирования (в частности, вертикального выравнивания данных), присущие только этому режиму. Для переключения между режимами отображения данных достаточно нажать клавишу [F4]. После выполнения этого действия окно задачника изменится следующим образом:</p><p><image l:href="#pt4exam1.png"/></p><p>В окне задачника можно просматривать все имеющиеся задания данной группы (нажимая клавиши [Enter] и [Backspace], а также генерировать различные варианты исходных данных и связанных с ними контрольных (т. е. правильных") результатов. При закрытии окна программа немедленно завершит работу, и мы вернемся в редактор среды PascalABC.NET. Заметим, что при последующих запусках программы будет автоматически выбираться тот режим окна задачника, в котором оно находилось в момент его предшествующего закрытия.</p><p>Для того чтобы cгенерировать html-страницу с описанием созданной группы (это позволяет, в частности, увидеть текст преамбулы группы), достаточно в процедуре Task тестирующей программы заменить символ ?" на символ "#": Task('ExamDemo#'). Теперь при запуске этой программы на экране вместо окна задачника с заданием ExamDemo14 появится html-браузер с описанием созданной группы:</p><p><image l:href="#pt4exam2.png"/></p><subtitle>Добавление новых заданий</subtitle><empty-line/><p>Добавим к нашей группе новые задания. Подобно заданиям, импортированным из группы ExamBegin, они будут посвящены преобразованию массивов путем перестановки их элементов. Если импортированные задания были посвящены инвертированию массива (или его части), то в новых заданиях надо будет выполнить перестановку всех пар элементов или перестановку первой и второй половины массива. Чтобы не уточнять действия в случае массивов нечетного размера, добавим в задание условие о том, что исходный массив всегда имеет четный размер. Тип элементов массива для подобных заданий является несущественным, поэтому будем обрабатывать массивы вещественных чисел. Таким образом, набор исходных данных будет иметь вид, подобный набору из задания ExamDemo2 (см. формулировку этого задания, приведенную на предыдущем рисунке). Оформление вывода результатов также не будет отличаться от оформления, требуемого в задании ExamDemo2.</p><p>Прежде чем приступать к реализации заданий, добавим в начало нашей библиотеки описание ряда вспомогательных переменных, процедур и функций.</p><p>Первая функция упрощает генерацию случайных исходных данных вещественного типа с не более чем двумя дробными знаками:</p><empty-line/><p><code>function RandR(a, b: integer): real;</code></p><p><code>begin</code></p><p><code>result := RandomN(a*100, b*100)/100;</code></p><p><code>end;</code></p><empty-line/><p>При реализации функции RandR мы использовали функцию RandomN, входящую в состав конструктора учебных заданий, начиная с версии 4.11 (функция RandomN(M, N) возвращает случайное целое число, лежащее в диапазоне M..N, включая границы диапазона). Функция RandR(a, b) (a и b -- целые) возвращает вещественное число, лежащее в диапазоне a..b и <emphasis>имеющее не более двух дробных знаков</emphasis>. Такие числа можно без потери точности записывать в текстовый файл в формате с двумя дробными знаками; таким образом, программа учащегося прочтет из файла именно то число, которое было сгенерировано при инициализации задания. Напомним, что в версии 4.11 конструктора имеется функция RandomR, также предназначенная для генерации случайных вещественных чисел, однако она не позволяет фиксировать число дробных знаков, и поэтому менее пригодна для генерации данных, предназначенных для записи в текстовые файлы.</p><p>Поскольку в обоих заданиях нам потребуется выполнять обмен значений, содержащихся в вещественных переменных, опишем процедуру, которая выполняет подобный обмен:</p><empty-line/><p><code>procedure SwapR(var a, b: real);</code></p><p><code>var</code></p><p><code>c: real;</code></p><p><code>begin</code></p><p><code>c := a;</code></p><p><code>a := b;</code></p><p><code>b := c;</code></p><p><code>end;</code></p><empty-line/><p>Следующая группа вспомогательных переменных и процедур связана с особенностью заданий типа Exam, описанной в правиле 5 (см. первый пункт данного раздела). Ввиду важности этого правила приведем его еще раз:</p><p>Набор исходных и контрольных данных надо сохранять в текстовых файлах, передавая задачнику информацию об именах этих файлов (процедурами DataS) и связывая содержимое этих файлов с разделами исходных и результирующих данных (процедурами DataFileT и ResultFileT соответственно). Таким образом, при инициализации каждого задания группы Exam надо выполнить следующие действия:</p><p>Сгенерировать имена файлов, содержащих исходные и контрольные данные (эти имена должны быть различными и меняться при каждом тестовом испытании программы; кроме того, подобно всем файлам, используемым в заданиях, они должны иметь расширение .tst). Связать созданные имена с файловыми переменными и открыть эти файлы на запись. Заполнить файлы необходимыми данными. Закрыть файлы с исходными и контрольными данными. Передать задачнику информацию об именах созданных файлов, чтобы при выполнении задания эта информация была использована при связывании файлов со стандартными потоками ввода-вывода. Передать задачнику информацию о том, что первый из созданных файлов должен быть включен в раздел исходных данных, а второй -- в раздел результатов; это, во-первых, позволит отобразить содержимое файлов в окне задачника и, во-вторых, обеспечит проверку правильности результирующего файла, созданного программой учащегося (путем его сравнения с данными контрольного файла). От условий конкретного задания будет зависеть только действие 3, связанное с заполнением файлов нужными данными. Все остальные действия являются стандартными и должны выполняться при инициализации любого задания групп Exam. Поэтому удобно оформить эти действия в виде двух вспомогательных процедур, одна из которых (StartExam) выполняет начальные действия 1-2, а другая (EndExam) -- завершающие действия 4-6. Поскольку в каждой из этих процедур необходимо использовать имена созданных файлов и связанные с ними файловые переменные, эти переменные удобно описать как глобальные:</p><empty-line/><p><code>var</code></p><p><code>f1,f2: text;</code></p><p><code>f1name, f2name: string;</code></p><empty-line/><p><code>procedure StartExam;</code></p><p><code>var</code></p><p><code>s: string;</code></p><p><code>begin</code></p><p><code>Str(RandomN(10000, 99999), s);</code></p><p><code>f1name := 'pt1' + s + '.tst';</code></p><p><code>f2name := 'pt2' + s + '.tst';</code></p><p><code>Assign(f1, f1name);</code></p><p><code>Rewrite(f1);</code></p><p><code>Assign(f2, f2name);</code></p><p><code>Rewrite(f2);</code></p><p><code>end;</code></p><empty-line/><p><code>procedure EndExam;</code></p><p><code>begin</code></p><p><code>Close(f1);</code></p><p><code>Close(f2);</code></p><p><code>DataS(f1name, 3, 1);</code></p><p><code>DataS(f2name, 45, 1);</code></p><p><code>DataFileT(f1name, 1, 5);</code></p><p><code>ResultFileT(f2name, 1, 5);</code></p><p><code>end;</code></p><empty-line/><p>Обсудим особенности этих процедур. Имена файлов, создаваемых в процедуре StartExam, имеют вид pt1#####.tst (для файла с исходными данными) и pt2#####.tst (для файла с контрольными данными), причем в позициях, помеченных символом #", располагаются цифры, выбираемые случайным образом. Тем самым обеспечиваются все требования к именам файлов: они генерируются случайным образом, имеют расширение .tst, и имя файла с исходными данными всегда отличается от имени контрольного файла. Напомним, что все файлы с расширением .tst автоматически удаляются из рабочего каталога после проверки учебного задания.</p><p>При анализе процедуры EndExam следует обратить внимание на то, что информация о содержимом исходного файла занимает всю область исходных данных (строки с первой по пятую -- см. вызов процедуры DataFileT) и, таким образом, она <emphasis>скрывает</emphasis> информацию об именах файлов, ранее выведенную в первой строке области исходных данных (см. вызовы процедур DataS). В обычном задании такая реализация была бы ошибочной, поскольку учащийся не увидел бы на экране имена файлов и не понял бы, что эти имена необходимо ввести и обработать в его программе. Однако в задании групп Exam именно такая реализация является правильной, поскольку ввод имен файлов и связывание этих файлов со стандартными потоками ввода-вывода выполняется автоматически (незаметно" для программы учащегося), и поэтому информацию об именах файлов на экране отображать не следует.</p><p>Итак, наличие процедур StartExam и EndExam позволяет нам упростить реализацию заданий: после определения формулировки любого задания нам достаточно вызвать процедуру StartExam, заполнить файлы f1 и f2 исходными и, соответственно, контрольными данными и вызвать процедуру EndExam.</p><p>Приступим к непосредственной реализации заданий. Поскольку эти задания являются однотипными, реализуем их в одной процедуре Exam1, снабдив ее параметром m: при m = 1 будет инициализироваться первое задание, а при m = 2 -- второе:</p><empty-line/><p><code>procedure Exam1(m: integer);</code></p><p><code>var</code></p><p><code>n, i: integer;</code></p><p><code>a: array[1..10] of real;</code></p><p><code>begin</code></p><p><code>CreateTask('Преобразование массивов');</code></p><p><code>case m of</code></p><p><code>1:</code></p><p><code>begin</code></p><p><code>TaskText('На вход в первой строке подается целое положительное четное число {N},', 0, 1);</code></p><p><code>TaskText('а во второй строке \= массив из {N} вещественных чисел. Поменять местами', 0, 2);</code></p><p><code>TaskText('его первый элемент со вторым, третий с четвертым, и т.\,д. Вывести', 0, 3);</code></p><p><code>TaskText('преобразованный массив в одной строке, для каждого элемента', 0, 4);</code></p><p><code>TaskText('отводить 7 экранных позиций.', 0, 5);</code></p><p><code>end;</code></p><p><code>2:</code></p><p><code>begin</code></p><p><code>TaskText('На вход в первой строке подается целое положительное четное число {N},', 0, 2);</code></p><p><code>TaskText('а во второй строке \= массив из {N} вещественных чисел. Поменять местами', 0, 3);</code></p><p><code>TaskText('первую и вторую половину элементов массива. Вывести преобразованный массив', 0, 4);</code></p><p><code>TaskText('в одной строке, для каждого элемента отводить 7 экранных позиций.', 0, 5);</code></p><p><code>end;</code></p><p><code>end;</code></p><p><code>StartExam;</code></p><p><code>n := 2 * RandomN(1, 5);</code></p><p><code>for i := 1 to n do</code></p><p><code>a[i] := RandR(-99, 99);</code></p><p><code>writeln(f1,n);</code></p><p><code>for i := 1 to n - 1 do</code></p><p><code>write(f1, a[i]:0:2, ' ');</code></p><p><code>writeln(f1, a[n]:0:2);</code></p><p><code>for i := 1 to n div 2 do</code></p><p><code>case m of</code></p><p><code>1: SwapR(a[2*i - 1], a[2*i]);</code></p><p><code>2: SwapR(a[i], a[i + n div 2]);</code></p><p><code>end;</code></p><p><code>for i := 1 to n do</code></p><p><code>write(f2, a[i]:7:2);</code></p><p><code>writeln(f2);</code></p><p><code>EndExam;</code></p><p><code>SetTestCount(3);</code></p><p><code>end;</code></p><empty-line/><p>Обратите внимание на то, что при вызове процедуры CreateTask ей передается строковый параметр, содержащий имя подгруппы "Преобразование массивов". Это обеспечивает включение новых заданий в подгруппу, с которой связаны ранее импортированные в нашу группу задания ExamBegin71 и ExamBegin72.</p><p>Размер исходного массива всегда будет четным и не превосходящим 10; последнее условие необходимо для того, чтобы все исходные данные можно было разместить на одной экранной строке.</p><p>В обоих заданиях значения элементов исходного массива можно выбирать произвольным образом из некоторого диапазона. Мы выбрали диапазон от -99 до 99, поскольку в этом случае при отображении чисел с двумя дробными знаками они будут занимать не более 6 экранных позиций.</p><p>При записи в файл элементов исходного массива между ними всегда располагается по одному пробелу, поскольку такой порядок организации исходных данных принят во всех заданиях групп ExamBegin и ExamTaskC. Чтобы обеспечить при этом отображение вещественных чисел с двумя дробными знаками, используется специальный набор форматирующих атрибутов: ":0:2". При выводе результатов, согласно формулировке задания, необходимо отводить для каждого элемента массива по 7 экранных позиций и выводить его с двумя дробными знаками (последнее условие принято по умолчанию во всех заданиях групп ExamBegin и ExamTaskC, использующих вещественные данные). Поэтому при выводе применяются другие форматирующие атрибуты: ":7:2".</p><p>Так как алгоритм решения обеих задач не содержит ветвлений, для проверки его правильности достаточно небольшого числа тестовых запусков. Мы установили это число равным трем, указав его в качестве параметра процедуры SetTestCount.</p><p>Нам осталось включить вызовы процедуры Exam1 (с параметрами, равными 1 и 2) в основную процедуру группы InitTask, связав эти вызовы с номерами заданий. Следует разместить новые задания сразу после импортированных заданий ExamBegin71 и ExamBegin72, так как все эти задания относятся к одной и той же подгруппе "Преобразование массивов". При этом номера последних 12 заданий увеличатся на 2:</p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('ExamBegin', 70 + num);</code></p><p><code>3..4: Exam1(num - 2);</code></p><p><code>5..16: UseTask('ExamTaskC', 20 + num);</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p>Необходимо также увеличить на 2 пятый параметр процедуры CreateGroup, определяющий общее количество заданий в группе (теперь это количество должно быть равно 16).</p><p>Для просмотра новых заданий в окне задачника надо заменить в параметре процедуры Task тестирующей программы символ "#" на "?": Task('ExamDemo?').</p><p>При нажатии клавиши [F9] мы увидим на экране окно задачника в демо-режиме, в котором можно выбрать и просмотреть все задания, включенные к настоящему моменту в нашу группу. Приведем вид окна для задания ExamDemo4 (напомним, что это задание инициализируется посредством вызова процедуры Exam1 с параметром, равным 2):</p><p><image l:href="#pt4exam3.png"/></p><subtitle>Добавление заданий повышенной сложности</subtitle><empty-line/><p>Наша группа ExamDemo к настоящему моменту содержит 12 заданий повышенной сложности, импортированных из группы ExamTaskC. Все эти задания связаны с общей предметной областью; они содержат сведения об абитуриентах и включают их фамилии, номера школ и годы поступления в вузы. Для того чтобы проиллюстрировать некоторые особенности, связанные с разработкой подобных заданий, дополним набор уже имеющихся заданий двумя новыми заданиями из той же предметной области.</p><p>Новые задания будут связаны с группировкой абитуриентов по школам: для каждой школы надо найти связанный с ней минимальный (или максимальный) год поступления абитуриента. Второе из двух заданий мы усложним, дополнительно потребовав, чтобы полученные результаты были отсортированы по убыванию максимального года (а для одинаковых годов -- по возрастанию номера школы). Первое задание сделаем более простым: в нем результирующие данные надо располагать по возрастанию номеров школ.</p><p>При генерации наборов исходных данных нам потребуются не только числа (номера школ и годы поступления), но и строковые данные -- фамилии абитуриентов (хотя для выполнения этих заданий они не требуются). Проще всего определить массив возможных фамилий достаточно большого размера, из которого выбирать элементы случайным образом. Заметим, что в условии заданий не говорится о том, что все фамилии в исходном наборе должны быть различными, поэтому совпадения фамилий вполне допустимы (если в некоторой группе заданий все фамилии должны быть уникальными, то целесообразно дополнять их <emphasis>инициалами</emphasis>, чтобы обеспечить большее разнообразие; кроме того, для таких заданий при добавлении к набору исходных данных новой фамилии необходимо проверять, что среди уже имеющихся элементов набора отсутствует данная фамилия с теми же инициалами).</p><p>Добавим к нашей библиотеке вспомогательный массив фамилий из 40 элементов (обратите внимание на то, что по правилам языка PascalABC.NET между описанием массива и списком инициализирующих значений указывается знак присваивания):</p><empty-line/><p><code>const</code></p><p><code>famcount = 40;</code></p><p><code>var</code></p><p><code>fam: array[1..famcount] of string :=</code></p><p><code>('Иванов', 'Петров', 'Сидоров', 'Кузнецов', 'Филиппов',</code></p><p><code>'Сергеев', 'Александров', 'Петухов', 'Пономарев', 'Яшин',</code></p><p><code>'Греков', 'Иванова', 'Кузнецова', 'Алексеева', 'Зайцев',</code></p><p><code>'Волкова', 'Фролов', 'Юрьев', 'Бондарев', 'Семенов',</code></p><p><code>'Семенова', 'Федченко', 'Марченко', 'Борисова', 'Петровский',</code></p><p><code>'Беляева', 'Белкин', 'Лысенко', 'Сорокина', 'Пастухов',</code></p><p><code>'Юрьева', 'Кондратьев', 'Тимофеев', 'Степанова', 'Якимов',</code></p><p><code>'Юсов', 'Степанов', 'Руденко', 'Демидов', 'Леонидов');</code></p><empty-line/><p>Оба новых задания, как и два предыдущих, мы реализуем в виде одной процедуры с параметром m, принимающим значения 1 или 2:</p><empty-line/><p><code>procedure Exam2(m: integer);</code></p><p><code>var</code></p><p><code>n, i, y, num, max, k: integer;</code></p><p><code>a: array[1..100] of integer;</code></p><p><code>nums: array[1..10] of integer;</code></p><p><code>begin</code></p><p><code>CreateTask('Обработка сложных наборов данных');</code></p><p><code>case m of</code></p><p><code>1:</code></p><p><code>begin</code></p><p><code>TaskText('На вход подаются сведения об абитуриентах. В первой строке указывается',0,1);</code></p><p><code>TaskText('количество абитуриентов {N}, каждая из последующих {N} строк имеет формат',0,2);</code></p><p><code>TaskText('\(\M<Год поступления> <Фамилия> <Номер школы>\m\)',0,3);</code></p><p><code>TaskText('Номер школы содержит не более двух цифр, годы лежат в диапазоне от 1990',0,4);</code></p><p><code>TaskText('до 2010. Для каждого номера школы, присутствующего в исходных данных,',0,5);</code></p><p><code>TaskText('определить связанный с ним минимальный год поступления (вначале указывать',0,0);</code></p><p><code>TaskText('номер школы, затем минимальный год). Сведения о каждой школе выводить',0,0);</code></p><p><code>TaskText('на новой строке и упорядочивать по возрастанию номера школы.',0,0);</code></p><p><code>k := 1;</code></p><p><code>for i := 1 to 100 do</code></p><p><code>a[i] := 2100;</code></p><p><code>end;</code></p><p><code>2:</code></p><p><code>begin</code></p><p><code>TaskText('На вход подаются сведения об абитуриентах. В первой строке указывается',0,1);</code></p><p><code>TaskText('количество абитуриентов {N}, каждая из последующих {N} строк имеет формат',0,2);</code></p><p><code>TaskText('\(\M<Номер школы> <Фамилия> <Год поступления>\m\)',0,3);</code></p><p><code>TaskText('Номер школы содержит не более двух цифр, годы лежат в диапазоне от 1990',0,4);</code></p><p><code>TaskText('до 2010. Для каждого номера школы, присутствующего в исходных данных,',0,5);</code></p><p><code>TaskText('определить связанный с ним максимальный год поступления (вначале указывать',0,0);</code></p><p><code>TaskText('максимальный год, затем номер школы). Сведения о каждой школе выводить',0,0);</code></p><p><code>TaskText('на новой строке и упорядочивать по убыванию максимального года,',0,0);</code></p><p><code>TaskText('а для совпадающих годов \= по возрастанию номера школы.',0,0);</code></p><p><code>k := -1;</code></p><p><code>for i := 1 to 100 do</code></p><p><code>a[i] := 0;</code></p><p><code>end;</code></p><p><code>end;</code></p><p><code>StartExam;</code></p><p><code>if Random(2)=0 then</code></p><p><code>n := RandomN(50, 100)</code></p><p><code>else</code></p><p><code>n := RandomN(10, 20);</code></p><p><code>case CurrentTest of</code></p><p><code>1: n := RandomN(10, 20);</code></p><p><code>2: n := RandomN(50, 100);</code></p><p><code>end;</code></p><p><code>if n <= 20 then</code></p><p><code>for i := 1 to 10 do</code></p><p><code>nums[i] := RandomN(1, 100);</code></p><p><code>writeln(f1,n);</code></p><p><code>for i := 1 to n do</code></p><p><code>begin</code></p><p><code>y := RandomN(1990, 2010);</code></p><p><code>if n <= 20 then</code></p><p><code>num := nums[RandomN(1, 10)]</code></p><p><code>else</code></p><p><code>num := RandomN(1, 100);</code></p><p><code>case m of</code></p><p><code>1: writeln(f1, y, ' ', fam[RandomN(1, famcount)],' ', num);</code></p><p><code>2: writeln(f1, num, ' ', fam[RandomN(1, famcount)],' ', y);</code></p><p><code>end;</code></p><p><code>if k*a[num] k*y then</code></p><p><code>a[num] := y;</code></p><p><code>end;</code></p><p><code>case m of</code></p><p><code>1: for i := 1 to 100 do</code></p><p><code>if a[i] < 2100 then</code></p><p><code>writeln(f2, i, ' ', a[i]);</code></p><p><code>2: while true do</code></p><p><code>begin</code></p><p><code>max := 0;</code></p><p><code>for i := 1 to 100 do</code></p><p><code>if a[i] max then</code></p><p><code>begin</code></p><p><code>max := a[i];</code></p><p><code>num := i;</code></p><p><code>end;</code></p><p><code>if max = 0 then</code></p><p><code>break</code></p><p><code>else</code></p><p><code>begin</code></p><p><code>writeln(f2, max, ' ', num);</code></p><p><code>a[num] := 0;</code></p><p><code>end;</code></p><p><code>end;</code></p><p><code>end;</code></p><p><code>EndExam;</code></p><p><code>SetTestCount(5);</code></p><p><code>end;</code></p><empty-line/><p>Обсудим детали реализации этих заданий. Начальная часть их формулировки посвящена описанию предметной области и является стандартной для данной серии заданий. Обратите внимание на то, что поля исходных записей в заданиях указываются в различном порядке. Этот прием используется во всех сериях группы ExamTaskC, чтобы обеспечить большее разнообразие входящих в них задач.</p><p>При определении завершающей части формулировки заданий последний параметр процедур TaskText полагается равным 0. Это означает, что в режиме окна с фиксированной компоновкой данные строки при первоначальном отображении задания не видны на экране, однако их можно просмотреть, используя <emphasis>прокрутку</emphasis> раздела с формулировкой задания. В режиме с динамической компоновкой полный текст формулировки задания сразу отображается на экране.</p><p>В процедуре используются два массива: массив a предназначен для хранения контрольных (правильных) результатов, а массив nums является вспомогательным (его назначение описывается далее). Для хранения исходных данных массив не предусматривается, поскольку после генерации полей очередной записи они будут немедленно записываться в исходный файл и обрабатываться.</p><p>Количество записей в исходном наборе данных записывается в переменную n. Наборы, содержащие небольшое число записей, удобны при отладке программы (благодаря своей "обозримости"), в то время как большие наборы позволяют проверить программу в "реальной" ситуации и тем самым окончательно убедиться в правильности алгоритма. Используя функцию CurrentTest, добавленную в версию 4.11 конструктора PT4TaskMaker, мы обеспечили дополнительную "настройку" процесса генерации исходных данных: при первом тестовом запуске программы с решением задачи ей всегда предлагается набор из небольшого количества записей (что упрощает поиск и исправление ошибок), а при втором тестовом запуске -- большой набор записей (что позволяет проверить предложенный алгоритм "на прочность"). При последующих тестовых запусках (а также при демонстрационном и ознакомительном запуске программы) значение n с равной вероятностью выбирается либо из диапазона 10..20, либо из диапазона 50..100.</p><p>В случае генерации исходных данных для указанных заданий при небольших значениях n (10-20) возникает дополнительная проблема: если выбирать случайным образом номера школ из всего допустимого диапазона 1-100, то с большой вероятностью каждый номер школы появится в наборе исходных данных всего по одному разу, что не позволит проверить правильность реализованного в программе алгоритма нахождения минимального/максимального значения. Чтобы решить эту проблему, используется вспомогательный массив nums из 10 элементов, в который заносятся 10 случайно выбранных номеров школ, после чего номера школ для исходного набора записей выбираются уже из этого набора номеров.</p><p>В любом задании, связанном с нахождением набора записей, обычно требуется <emphasis>отсортировать</emphasis> полученный набор. Задание должно быть сформулировано таким образом, чтобы обеспечить <emphasis>однозначный</emphasis> порядок вывода полученных данных. В частности, если поле, по которому выполняется сортировка (<emphasis>главный ключ сортировки</emphasis>), может содержать одинаковые значения, то обязательно следует указать дополнительное поле (<emphasis>подчиненный ключ сортировки</emphasis>), по которому надо сортировать записи с одинаковым главным ключом. При выводе отсортированных данных вначале надо располагать главный ключ, после него -- подчиненные ключи (если они имеются), затем -- остальные поля (такой порядок вывода принят во всех заданиях группы ExamTaskC).</p><p>Для упорядочивания результатов во втором задании вместо сортировки массива a по убыванию используется другой алгоритм, связанный с последовательным нахождением максимального элемента, выводом этого элемента и его порчей" (заменой его значения на 0). Обычная сортировка массива в данном случае не позволит получить требуемый набор данных, так как при перемене местами значений элементов в массиве a будет потеряна связь с номером школы (который определяется по индексу элемента). Заметим, что возможен и вариант получения упорядоченного набора данных с помощью сортировки, однако для этого надо использовать массив <emphasis>записей</emphasis>, полями которых являются максимальный год и номер школы.</p><p>Во втором задании результаты должны упорядочиваться по <emphasis>набору ключей</emphasis>: первый (главный) ключ -- максимальный год (сортируется по убыванию), второй (подчиненный) ключ -- номер школы (сортируется по возрастанию). Использованный нами способ упорядочивания обеспечивает <emphasis>автоматическую</emphasis> сортировку по подчиненному ключу, так как при поиске очередного максимума массив a перебирается по <emphasis>возрастанию</emphasis> индексов, и поэтому в результате находится номер <emphasis>первого</emphasis> максимального элемента.</p><p>Прочие фрагменты процедуры Exam2 дополнительных комментариев не требуют.</p><p>Вызов процедуры Exam2 надо добавить в конец оператора case процедуры InitTask, связав его с номерами 17 и 18:</p><empty-line/><p><code>procedure InitTask(num: integer);</code></p><p><code>begin</code></p><p><code>case num of</code></p><p><code>1..2: UseTask('ExamBegin', 70 + num);</code></p><p><code>3..4: Exam1(num - 2);</code></p><p><code>5..16: UseTask('ExamTaskC', 20 + num);</code></p><p><code>17..18: Exam2(num - 16);</code></p><p><code>end;</code></p><p><code>end;</code></p><empty-line/><p>Кроме того, необходимо опять откорректировать параметр процедуры CreateGroup, определяющий количество заданий, положив его равным 18.</p><p>При нажатии клавиши [F9] на экране появится окно задачника с последним заданием данной группы (на рисунке приведен вид окна после прокрутки раздела с формулировкой задания):</p><p><image l:href="#pt4exam4.png"/></p><p>Если теперь опять заменить символ ?" на символ "# " в параметре процедуры Task тестирующей программы, то при нажатии [F9] мы увидим html-страницу с описанием группы, которое теперь содержит не только импортированные, но и реализованные нами задания:</p><p><image l:href="#pt4exam5.png"/></p><p>Поскольку при создании новых заданий мы указали в качестве параметра процедур CreateTask названия подгрупп (и разместили новые задания после заданий из данных подгрупп), новые задания отображаются в составе этих подгрупп: ExamDemo3 и ExamDemo4 -- в подгруппе "Преобразование массивов", а ExamDemo17 и ExamDemo18 -- в подгруппе "Обработка сложных наборов данных".</p></section></section></section><section><title><p>Уроки PascalABC .NET</p></title><section><title><p>ABCObjects: быстрое введение</p></title><p>Основными типами графических объектов, определенными в модуле ABCObjects, являются RectangleABC, SquareABC, EllipseABC, CircleABC, TextABC, RegularPolygonABC, StarABC, PictureABC, MultiPictureABC, BoardABC и ContainerABC.</p><p>Типы графических объектов представляют собой классы, состоящие из методов и свойств, а также нуждающиеся в конструировании перед первым использованием. Изменение свойств влияет на внешний вид и поведение графических объектов. Например, при изменении свойств Width и Height меняются размеры графического объекта, при изменении свойства Color - цвет графического объекта и т.д. Вызов методов графического объекта возвращает или меняет его характеристики. Например, при вызове метода ToFront графический объект перемещается на передний план, а вызов метода Intersect(g) возвращает, пересекается ли текущий объект с объектом g.</p><p>Все графические объекты являются разновидностями класса ObjectABC, который содержит общие для всех свойства и методы.</p><p>Создадим два перекрывающихся графических объекта:</p><cite><p><strong>uses</strong> ABCObjects,GraphABC;</p><p><strong>var</strong></p><p> r: RectangleABC;</p><p> c: CircleABC;</p><p><strong>begin</strong></p><p> r := <strong>new</strong> RectangleABC(70,50,200,100,clMoneyGreen);</p><p> c := <strong>new </strong>CircleABC(120,80,110,clBlue);</p><p><strong>end</strong>.</p></cite><p>После запуска программы увидим на экране следующее:</p><image l:href="#abc1.png"/><p>Поменяем некоторые свойства графических объектов и вызовем метод MoveOn для окружности, дописав в конец программы следующие строки:</p><cite><p>r.Width := 150;</p><p>c.Color := clRed;</p><p>c.MoveOn(30,30);</p></cite><p>После запуска программы:</p><image l:href="#abc2.png"/><p>Добавим в конец программы следующие строки:</p><cite><p>c.Number := 8;</p><p>r.Text := 'Hello';</p><p>r.ToFront;</p></cite><p>После запуска программы:</p><image l:href="#abc3.png"/></section><section><title><p>ABCObjects: контейнеры графических объектов</p></title><p>Класс ContainerABC представляет собой контейнер графических объектов. Он также является потомком ObjectABC, но при создании не содержит ни одного объекта. Он добавляет следующий интерфейс:</p><cite><p><strong>procedure</strong> Add(g: ObjectABC);</p><p><strong>property</strong> Count: integer; // количество объектов</p><p><strong>property</strong> Objects[i: integer]: ObjectABC; // i-тый объект</p></cite><p>При масштабировании ContainerABC производится масштабирование всех входящих в него объектов. При добавлении объекта в ContainerABC его свойство Owner становится равным этому ContainerABC. При присваивании свойству Owner объекта его владелец меняется, при этом объект перерисовывается как принадлежащий новому владельцу. При присваивании свойству Owner объекта значения <strong>nil</strong> он перестает иметь владельца и отображается непосредственно в графическом окне.</p><p>Рассмотрим следующую программу:</p><cite><p><strong>uses</strong> ABCObjects,GraphABC;</p><p><strong>var</strong></p><p> c1,c2: ContainerABC;</p><p> r: CircleABC;</p><p><strong>begin</strong></p><p> SetWindowSize(300,300);</p><p> c1 := <strong>new</strong> ContainerABC(50,30);</p><p> c1.Add(<strong>new </strong>RectangleABC(0,0,200,100,clGreen));</p><p> r := <strong> new </strong>CircleABC(15,15,70,clYellow);</p><p><strong>end</strong>.</p></cite><p>После ее запуска графический экран имеет вид:</p><p><image l:href="#cont1.png"/></p><p>Контейнер c1 содержит зеленый прямоугольник, а объект r не имеет владельца (r.Owner=nil). Нетрудно убедиться, что ObjectsCount=2 (контейнер и круг), а c1.Count=1.</p><p>Добавим круг в контейнер, дописав в конец программы строчку</p><cite><p>c1.Add(r);</p></cite><p>После запуска программы графический экран примет вид:</p><p><image l:href="#cont2.png"/></p><p>Круг r теперь принадлежит контейнеру (r.Owner=с2), ObjectsCount=1 (только контейнер), а c1.Count=2. Кроме этого, координаты круга пересчитываются относительно координат контейнера-владельца (они по-прежнему равны (15,15), но относительно левого верхнего угла контейнера c1).</p><p>Такой же эффект можно было получить от оператора</p><cite><p>r.Owner := c1;</p></cite><p>Создадим второй контейнер c2 и поменяем владельца у r на c2. Для этого допишем в конец строки:</p><cite><p>c2 := <strong>new</strong> ContainerABC(50,160);</p><p>c2.Add(<strong>new</strong> RectangleABC(0,0,200,100,clMoneyGreen));</p><p>r.Owner := c2;</p></cite><p>После запуска программы графический экран примет вид:</p><p><image l:href="#cont3.png"/></p><p>Как мы видим, круг r поменял владельца, и теперь имеет координаты (15,15), но относительно левого верхнего угла нового владельца c2.</p><p>Если вместо строчки r.Owner:=c2; написать r.Owner:=<strong>nil</strong>; , то круг r потеряет владельца и снова будет позиционироваться относительно левого верхнего угла экрана:</p><p><image l:href="#cont4.png"/></p><subtitle>Примеры</subtitle></section><section><title><p>Графика и анимация</p></title><section><title><p>Анимация без мерцания</p></title><p>Данная программа иллюстрирует применение процедур LockDrawing и Redraw для реализации анимации без мерцания:</p><empty-line/><empty-line/><p><code><strong>uses</strong> GraphABC;</code></p><p><code><strong>begin</strong></code></p><p><code>LockDrawing;</code></p><p><code><strong>for var</strong> i:=1 to 500 do</code></p><p><code><strong>begin</strong></code></p><p><code>Window.Clear;</code></p><p><code>Brush.Color := clGreen;</code></p><p><code>Ellipse(i,100,i+100,200);</code></p><p><code>Redraw;</code></p><p><code>Sleep(1);</code></p><p><code><strong>end</strong>;</code></p><p><code><strong>end</strong>.</code></p><empty-line/><p>Основная идея состоит в следующем: отключим рисование на экране, вызвав LockDrawing (рисование будет осуществляться только во внеэкранном буфере), после чего будем всякий раз формировать новый кадр изображения и выводить его целиком на экран, вызывая Redraw. При вызове Redraw перерисовывается все графическое окно, поэтому скорость анимации ограничена скоростью вывода внеэкранного буфера на экран.</p></section></section><section><title><p>Простейшие события</p></title><section><title><p>Рисование мышью в графическом окне</p></title><p>Данная программа осуществляет рисование мышью в графическом окне:</p><empty-line/><empty-line/><p><code><strong>uses</strong> GraphABC;</code></p><empty-line/><p><code>procedur<strong>e</strong> MouseDown(x,y,mb: integer);</code></p><p><code><strong>begin</strong></code></p><p><code>MoveTo(x,y);</code></p><p><code><strong>end</strong>;</code></p><p><code/></p><p><code>procedur<strong>e</strong> MouseMove(x,y,mb: integer);</code></p><p><code><strong>begin</strong></code></p><p><code><strong>if</strong> mb=1 <strong>then</strong> LineTo(x,y);</code></p><p><code><strong>end</strong>;</code></p><p><code/></p><p><code>begin</code></p><p><code> // Привязка обработчиков к событиям</code></p><p><code>OnMouseDown := MouseDown;</code></p><p><code>OnMouseMove := MouseMove</code></p><p><code><strong>end</strong>.</code></p><empty-line/></section><section><title><p>Перемещение окна с помощью клавиатуры</p></title><p>Данная программа осуществляет перемещение графического окна с помощью клавиатуры:</p><empty-line/><empty-line/><p><code><strong>uses</strong> GraphABC;</code></p><p><code/></p><p><code>procedur<strong>e</strong> KeyDown(Key: integer);</code></p><p><code><strong>begin</strong></code></p><p><code><strong> case</strong> Key <strong>of</strong></code></p><p><code>VK_Left: Window.Left := Window.Left - 2;</code></p><p><code>VK_Right: Window.Left := Window.Left + 2;</code></p><p><code>VK_Up: Window.Top := Window.Top - 2;</code></p><p><code>VK_Down: Window.Top := Window.Top + 2;</code></p><p><code><strong> end</strong>;</code></p><p><code><strong>end</strong>;</code></p><p><code/></p><p><code>begi<strong>n</strong></code></p><p><code>// Привязка обработчиков к событиям</code></p><p><code>OnKeyDown := KeyDown;</code></p><p><code><strong>end</strong>.</code></p><empty-line/></section></section><section><title><p>Пример использования таймера</p></title><p>Данная программа выводит 1 каждые 100 миллисекунд в течение 3 секунд:</p><empty-line/><empty-line/><p><code><strong>uses</strong> Timers;</code></p><empty-line/><p><code>procedur<strong>e</strong> TimerProc;</code></p><p><code><strong>begin</strong></code></p><p><code>write(1);</code></p><p><code><strong>end</strong>;</code></p><p><code/></p><empty-line/><p><code>begin</code></p><p><code><strong>var</strong> t := <strong>new</strong> Timer(100,TimerProc);</code></p><p><code>t.Start;</code></p><p><code>Sleep(3000);</code></p><p><code><strong>end</strong>.</code></p><empty-line/><p>Вызов Sleep здесь обязателен, иначе программа после создания таймера сразу закончится, и обработчик таймера ни разу не сработает.</p></section></section></section></body>

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных