diff --git a/Configuration/GlobalAssemblyInfo.cs b/Configuration/GlobalAssemblyInfo.cs index 8b571d2f9..d75b85bff 100644 --- a/Configuration/GlobalAssemblyInfo.cs +++ b/Configuration/GlobalAssemblyInfo.cs @@ -15,7 +15,7 @@ internal static class RevisionClass public const string Major = "3"; public const string Minor = "11"; public const string Build = "0"; - public const string Revision = "3699"; + public const string Revision = "3700"; public const string MainVersion = Major + "." + Minor; public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision; diff --git a/Configuration/Version.defs b/Configuration/Version.defs index f1e858597..2bad05f96 100644 --- a/Configuration/Version.defs +++ b/Configuration/Version.defs @@ -1,4 +1,4 @@ -%MINOR%=11 -%REVISION%=3699 %COREVERSION%=0 +%REVISION%=3700 +%MINOR%=11 %MAJOR%=3 diff --git a/PABCNetHelp/ForEducation/CheckedTasks/checkedtasks2.html b/PABCNetHelp/ForEducation/CheckedTasks/checkedtasks2.html index f4c0268b9..d42381fcb 100644 --- a/PABCNetHelp/ForEducation/CheckedTasks/checkedtasks2.html +++ b/PABCNetHelp/ForEducation/CheckedTasks/checkedtasks2.html @@ -13,7 +13,7 @@ Приведем формулировку этого задания.

-

a1. Закрасить помеченные клетки.

+

RBa1. Закрасить помеченные клетки.

diff --git a/PABCNetHelp/ForEducation/CheckedTasks/ctexam.html b/PABCNetHelp/ForEducation/CheckedTasks/ctexam.html deleted file mode 100644 index c2d884308..000000000 --- a/PABCNetHelp/ForEducation/CheckedTasks/ctexam.html +++ /dev/null @@ -1,654 +0,0 @@ - - - - - - - - - - - -

Задания, связанные с ЕГЭ по информатике -

- - - -
-

Пример 1. Простая задача на реализацию базовых алгоритмов -

-
-

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

-

ExamBegin2°. На вход подаются три вещественных числа; числа расположены в одной строке. -Вывести вначале минимальное, а затем максимальное из них. Каждое число должно выводиться на новой строке -и снабжаться комментарием: «MIN=» для минимального, «MAX=» -для максимального. -

-
-

Создание программы-заготовки и знакомство с заданием -

-

Напомним, что программу-заготовку для решения этого задания можно создать с помощью -команды -меню «Модули | Создать шаблон программы», кнопки - или клавиатурной -комбинации Shift+Ctrl+L. Приведем текст созданной заготовки: -

-
uses PT4Exam; - -

begin
-
  - Task('ExamBegin2'); - -
end
. -
-

После запуска программы на экране появится окно задачника: -

-
- -
- -

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

-

В программе-заготовке вместо модуля PT4 -подключается модуль PT4Exam, специально предназначенный для использования при выполнении -заданий групп Exam. Данный модуль содержит реализацию единственной процедуры Task, -инициализирующей задание. Никакие дополнительные процедуры, связанные с вводом-выводом, в него не включены. -Это обусловлено тем, что ввод-вывод при выполнении заданий групп Exam надо выполнять, используя -стандартные процедуры языка Pascal. -

-

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

-

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

-

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

-
-

Ввод исходных данных и их обработка -

-

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

-
uses PT4Exam; -

begin

  - Task('ExamBegin2'); -
  -var - (a,b,c) := ReadReal3; -
end
. -
-

Мы воспользовались стандартной функцией ввода ReadReal3, введя все три исходных числа за один ее вызов. -

-

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

-

Реализуем алгоритм нахождения минимального и максимального элемента. Для этого опишем еще две -переменные min и max типа real и добавим в конец программы следующие операторы: -

-
if a < b then -
begin
-
  - min := a; -
  max := b; -
end
-
else
-
begin

  - min := b; -
  max := a; -
end
; -
if
c < min then
  - min := c -
else
- if c > max then
  - max := c; - -
-

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

- -
-

Вывод результатов и их форматирование -

-

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

-

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

-
Println(min, max);
-

Приведем вид окна задачника при запуске полученной программы: -

- -
- -
- -

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

-

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

-

Для исправления первых двух ошибок достаточно изменить вывод следующим образом: -

-
Println('MIN=', min);
Println('MAX=', max);
-

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

-
Println('MIN=', min:0:2);
Println('MAX=', max:0:2);
-

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

-

При запуске исправленной программы будет выведено сообщение о верном решении, а после трех -запусков — сообщение о том, что задание выполнено: -

- -
- -
- - -

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

- -

-Примечание. -В системе PascalABC.NET, -благодаря специальному механизму перенаправления данных, стандартные процедуры read-write можно использовать -и при выполнении заданий из других групп, однако важно учитывать ряд особенностей использования этих процедур -в заданиях групп Exam. Во-первых, только в заданиях групп Exam будут проявляться различия в использовании -процедур read-write и readln-writeln (в остальных группах для ввода-вывода можно использовать как вариант -процедуры с суффиксом «ln», так и вариант без этого суффикса). Во-вторых, только в заданиях -групп Exam можно при необходимости использовать атрибуты форматирования при выводе результатов, -а также выводить дополнительные комментарии, если этого требует условие задачи. В-третьих, -только в заданиях групп Exam можно вводить и выводить элементы данных несколькими способами, с использованием переменных -различных типов; например, исходную строку можно либо сразу прочесть в строковую переменную, либо вводить -посимвольно в цикле (в других группах заданий проводится более строгая проверка на соответствие типа переменной -типу элемента исходных или результирующих данных). -

- - -
-

Пример 2. Ввод и вывод массивов -

-
- -

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

- -

ExamBegin28°. На вход в первой строке подаются два целых положительных числа M и N, -во второй строке — вещественное число D, а в третьей строке — набор из M вещественных чисел. -Сформировать и вывести двумерный вещественный массив размера M × N, у которого первый -столбец совпадает с исходным набором чисел, а элементы каждого следующего столбца равны сумме соответствующего -элемента предыдущего столбца и числа D (в результате каждая строка массива будет содержать -элементы арифметической прогрессии). Каждую строку элементов массива выводить на новой экранной строке, -для каждого числа отводить 7 экранных позиций. -

- - -

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

- -
- -
- - -

Анализируя исходные данные, можно заметить, что полученная матрица должна иметь 8 строк, тогда как -на экране отображаются только первые пять. -Это связано с тем, что по умолчанию используется режим «свернутого» отображения данных, при котором -на экране выводится только несколько начальных строк. Признаком того, что имеются данные, -не выведенные на экране, является кнопка, которая отображается в правом верхнем углу раздела исходных данных -(на этой кнопке изображается стилизованная стрелка, направленная вниз). -Для вывода всех данных достаточно нажать эту кнопку; можно также нажать клавишу Ins или выполнить -щелчок мышью в любом месте раздела с данными задания (кроме раздела, содержащего формулировку). -Если выполнить эти действия для нашего окна, то оно изменится следующим образом:

-
- -
-

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

- -

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

- -

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

- -

Приведем вариант правильного решения данной задачи (в этом варианте учитывается, что результирующий -двумерный массив имеет не более 10 строк и столбцов; соответствующее условие приведено в -преамбуле к описанию -группы ExamBegin): -

- -
uses PT4Exam;
-var
-  m, n, i, j: integer;
-  d: real;
-  a: array[1..10, 1..10] of real;
-begin
-  Task('ExamBegin28');
-  readln(m, n, d);
-  for i := 1 to m do
-    read(a[i, 1]);
-  for j := 2 to n do
-    for i := 1 to m do
-      a[i, j] := a[i, j - 1] + d;
-  for i := 1 to m do
-  begin
-    for j := 1 to n do
-      write(a[i, j]:7:2);
-    writeln;
-  end;
-end.
-
- -

В приведенном решении следует обратить особое внимание на организацию ввода-вывода. -Укажем две особенности, связанные с вводом. Во-первых, несмотря -на то что по условию число d находится во второй строке, мы смогли включить его в один список с -предшествующими числами m и n (поскольку при чтении числовых данных переход на новую строку выполняется -автоматически). Во-вторых, при чтении элементов исходного набора необходимо использовать вариант -процедуры read без суффикса «ln», чтобы не пропустить оставшиеся в этой строке числа. -

-

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

-

-Примечание. -Заметим, что в заданиях группы Matrix, -также посвященной обработке двумерных массивов, специальные действия по форматированию полученных массивов -выполнять не требуется, так как задачник автоматически форматирует все полученные результаты. -Таким образом, задания группы ExamBegin «более приближены» к реальной экзаменационной ситуации, -в которой программа учащегося должна не только обрабатывать исходные данные, но и обеспечивать наглядное -отображение результатов. -

- -
- -

Пример 3. Обработка сложных наборов данных -

-
- -

Группа ExamTaskC содержит 100 типовых заданий, аналогичных заданиям, -которые предлагаются на ЕГЭ по информатике -в качестве задач повышенной сложности (задача C4). Основную часть данной группы составляют -задания на обработку сложных наборов данных (записей) с элементами-полями различных типов. -В подобных заданиях требуется правильно выбрать способ хранения данных и организовать их эффективную обработку; -при этом обычно требуется применить несколько базовых алгоритмов, например, алгоритм суммирования -или нахождения минимума/максимума и алгоритм поиска нужного элемента или сортировки набора данных по требуемому ключу. -В группу ExamTaskC включены также задания повышенной сложности на обработку текстовых данных -(подобные задания содержатся в завершающем разделе данной группы). -

-

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

-

В заданиях группы ExamTaskC ввод и вывод имеет те же особенности, -что и в заданиях группы ExamBegin. -

-

Рассмотрим следующее задание. -

- -

ExamTaskC25°. На вход подаются сведения об абитуриентах. В первой строке -указывается количество абитуриентов N, каждая из последующих N строк имеет -формат
<Номер школы> <Год поступления> <Фамилия>
-Номер школы содержит не более двух цифр, годы лежат в диапазоне от 1990 до 2010. Для каждого года, -присутствующего в исходных данных, вывести общее число абитуриентов, поступивших в этом году -(вначале выводить год, затем число абитуриентов). Сведения о каждом годе выводить на новой строке -и упорядочивать по возрастанию номера года.

- - -

Программа-заготовка, созданная для этого задания, подобно заготовкам для заданий группы ExamBegin, -будет использовать специальный модуль PT4Exam: -

- -
uses PT4Exam;
-
-begin
-  Task('ExamTaskC25');
-
-end.
-
- -

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

- -
- -
- -

Окно будет иметь такой вид, если при его предшествующем закрытии оно находилось в режиме -отображения всех данных. -Для отображения всех данных на экране может потребоваться увеличить высоту окна; для этого достаточно -зацепить мышью заголовок окна и переместить его вверх (для перемещения заголовка окна задачника вверх и вниз -можно также воспользоваться клавиатурными комбинациями Ctrl+Up и Ctrl+Down). -

-

При первом тестовом испытании программы ей будет предложен для обработки -набор данных не слишком большого размера (порядка 10–20 элементов). -

-

Вначале следует определиться со структурами данных, которые будут использоваться в программе. Поскольку -требуется найти одну характеристику для каждого года, а число лет невелико, можно использовать -числовой массив year, каждый элемент которого соответствует определенному году. Так как в языке Pascal можно -использовать произвольные границы индексов, удобно в качестве диапазона индексов указать диапазон лет, -который требуется проанализировать: 1990..2010. В начале программы выполним инициализацию элементов массива, -положив их значения равными 0 -(заметим, что если после обработки исходных данных некоторые элементы массива year -останутся нулевыми, то это будет означать, что соответствующие годы не были представлены в наборе исходных данных, -и выводить информацию о них не следует). -

- -

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

- -

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

- -

Приведем первый вариант решения (этот вариант содержит одну ошибку): -

- -
uses PT4Exam;
-var
-  n, i, k, m: integer;
-  year: array[1990..2010] of integer;
-begin
-  Task('ExamTaskC25');
-  for i := 1990 to 2010 do
-    year[i] := 0;
-  readln(n);
-  for i := 1 to n do
-  begin
-    readln(k, m); { k - номер школы, m - год поступления }
-    Inc(year[m]);
-  end;
-  for i := 1990 to 2010 do
-    writeln(i, ' ', year[i]);
-end.
-
- -

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

- -
- -
-

Для исправления ошибки достаточно добавить в последний цикл условный оператор: -

- -
for i := 1990 to 2010 do
-  if year[i] > 0 then
-    writeln(i, ' ', year[i]);
-
- - -

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

-

Завершая рассмотрение этого задания, опишем некоторые дополнительные возможности, связанные -с просмотром больших наборов данных. -

-

Начиная со второго испытания, -программе может быть предложен для обработки набор исходных данных большего размера (порядка 50–100 -элементов). При этом уже не удастся отобразить на экране все данные, связанные с заданием. -В подобной ситуации у правой границы окна задачника появится полоса прокрутки, позволяющая -перемещаться к той части данных, которая первоначально не отображается на экране. -Прокрутку данных можно выполнять не только с помощью полосы прокрутки, но и используя -клавиши со стрелками, PgUp, PgDn, Home, End, а также колесико мыши. -

-

Помимо стандартных действий по прокрутке данных, в окне задачника предусмотрены возможности -«интеллектуальной» прокрутки, -позволяющие быстро перейти к началу каждого раздела задания, а также сравнить соответствующие фрагменты -полученных результатов и примера верного решения. Для циклического перебора разделов сверху вниз -предназначена клавиша [+] (а также комбинация Ctrl+PgDn), для циклического перебора разделов снизу вверх -— клавиша [–] (а также комбинация Ctrl+PgUp). Для быстрого переключения между -соответствующими фрагментами разделов с результатами и с примером верного решения предназначена клавиша [/] -(а также комбинация Ctrl+Tab). Все эти действия можно выполнить и с помощью мыши; для этого -предусмотрены кнопки в левом верхнем углу прокручиваемой области окна, отведенной под отображение разделов задания -(эти кнопки отображаются -на экране, если размер данных, связанных с заданием, превышает размеры окна). -Приведем вид окна задачника с полосой прокрутки и дополнительными кнопками: -

-
- -
-

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

-

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

-

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

- -
-

Пример 4. Более сложное задание на обработку наборов данных -

-
- -

ExamTaskC53°. На вход подаются сведения о ценах на бензин на автозаправочных станциях (АЗС). -В первой строке содержится значение M одной из марок бензина, во второй строке указывается -целое число N, а каждая из последующих N строк имеет формат
-<Марка бензина> <Улица> <Компания> <Цена 1 литра (в копейках)>
-Имеется не более 20 различных компаний и не более 30 различных улиц; названия компаний и улиц -не содержат пробелов. В качестве марки бензина указываются числа 92, 95 или 98. Цена задается целым числом -в диапазоне от 2000 до 3000. Каждая компания имеет не более одной АЗС на каждой улице; -цены на разных АЗС одной и той же компании могут различаться. Для каждой улицы, на которой имеются -АЗС с бензином марки M, определить максимальную цену бензина этой марки (вначале выводить максимальную -цену, затем название улицы). Сведения о каждой улице выводить на новой строке и упорядочивать -по возрастанию максимальной цены, а для одинаковой цены — по названиям улиц в алфавитном порядке. -Если ни одной АЗС с бензином марки M не найдено, то вывести текст «Нет». -

- - - -

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

-
- -
- - -

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

- -

Определим запись Street с двумя полями name и max и опишем массив s -из 30 элементов типа Street. -Следует также завести переменную ns, в которой будет храниться количество заполненных элементов массива s. -

- -

При обработке каждой строки с исходными данными нам будут нужны прежде всего сведения о марке бензина. -Если марка бензина не равна M, то оставшуюся часть строки обрабатывать не требуется, и можно сразу перейти -к разбору следующей строки. Если марка бензина равна M, то необходимо узнать название улицы s0 и цену бензина p. -Заметим, что название компании для решения задачи не требуется, однако его необходимо прочесть, чтобы определить -следующий элемент данных — цену бензина. -

- -

Если улица с названием s0 еще не была включена в массив s, то ее необходимо включить в массив, присвоив -полю max значение p. Если же улица уже присутствует в массиве, то необходимо сравнить поле max для данной улицы -и значение p, изменив при необходимости поле max (здесь мы используем базовый алгоритм нахождения -максимального значения). -

- -

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

- -

После обработки набора исходных данных необходимо проверить, найдена ли хотя бы одна улица с АЗС, -предлагающей марку бензина M (для этого достаточно сравнить значение ns с нулем). Если ни одна улица -не найдена, то надо вывести строку «Нет»; в противном случае требуется выполнить сортировку -массива s по указанному набору ключей и вывести полученные данные в требуемом порядке. Поскольку размер -массива невелик, для его сортировки вполне допустимо использовать один из простых алгоритмов, -например, алгоритм пузырьковой сортировки. -

- -

Приведем один из вариантов правильного решения задачи: -

- -
uses PT4Exam;
-type
-  Street = record
-    name: string;
-    max: integer;
-  end;
-var
-  m, n, ns, i, j, k, p: integer;
-  s: array[1..30] of Street;
-  s0: string;
-  x: Street;
-  c: char;
-begin
-  Task('ExamTaskC53');
-  readln(m); { m - марка бензина }
-  readln(n);
-  ns := 0;
-  for i := 1 to n do
-  begin
-    read(k);
-    if k <> m then
-      readln { пропускаем оставшуюся часть строки }
-    else
-    begin
-      s0 := '';
-      read(c); { пропускаем пробел после первого числа }
-      read(c); { читаем первый символ названия улицы }
-      while c <> ' ' do
-      begin
-        s0 := s0 + c;
-        read(c);
-      end;
-      read(c); { читаем первый символ названия компании }
-      while c <> ' ' do
-        read(c); { название компании не сохраняем }
-      readln(p); { читаем цену бензина и переходим на новую строку }
-      { Обработка прочитанной информации }
-      k := 0;
-      for j := 1 to ns do
-        if s[j].name = s0 then { улица уже содержится в массиве s }
-        begin
-          k := 1;
-          if s[j].max < p then
-            s[j].max := p;
-          break;
-        end;
-      if k = 0 then { улица еще не содержится в массиве s }
-      begin
-        Inc(ns);
-        s[ns].name := s0;
-        s[ns].max := p;
-      end;
-    end;
-  end;
-  if ns = 0 then { ни одной улицы не найдено }
-    writeln('Нет')
-  else
-  begin
-    { Сортировка по возрастанию максимальной цены,
-      а для одинаковых цен - по названиям улиц }
-    for k := 1 to ns - 1 do
-      for i := 1 to ns - k do
-        if (s[i].max > s[i + 1].max) or
-           (s[i].max = s[i + 1].max) and
-           (s[i].name > s[i + 1].name) then
-        begin
-          x := s[i];
-          s[i] := s[i + 1];
-          s[i + 1] := x;
-        end;
-    { Вывод результатов в требуемом порядке }
-    for i := 1 to ns do
-      writeln(s[i].max,' ',s[i].name);
-  end;
-end.
-
- - diff --git a/PABCNetHelp/ForEducation/CheckedTasks/ctfile.html b/PABCNetHelp/ForEducation/CheckedTasks/ctfile.html deleted file mode 100644 index 979cc5c2c..000000000 --- a/PABCNetHelp/ForEducation/CheckedTasks/ctfile.html +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - -

Задание на обработку файлов

- -
-

Создание программы-заготовки и знакомство с заданием

- -

В качестве примера задания на обработку файлов рассмотрим задание File48.

-

Напомним, что программу-заготовку для решения этого задания можно создать с помощью -команды -меню «Модули | Создать шаблон программы», кнопки - или клавиатурной -комбинации Shift+Ctrl+L. Эта заготовка будет иметь следующий вид: -

- -
-
-uses PT4;
-
-begin
-  Task('File48');
-
-end.
-
- -

После запуска данной программы на экране появится окно задачника:

- -
- -
- - -

В первой строке раздела исходных данных указаны имена трех исходных файлов (SA, SB -и SC) и одного результирующего (SD). В последующих строках раздела исходных данных -показано содержимое исходных файлов. -Элементы файлов отображаются бирюзовым цветом, чтобы подчеркнуть их отличие от обычных исходных данных (желтого цвета) -и комментариев (светло-серого цвета).

-

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

-

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

-

- - -
-

Ввод части исходных данных

-

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

- -
-
-uses PT4;
-var
-  i: integer;
-  s: string;
-  f: array [1..4] of file of integer;
-begin
-  Task('File48');
-  for i := 1 to 3 do
-  begin
-    read(s);
-    Assign(f[i], s);
-  end;
-end. 
-
- - -

Мы намеренно ограничились тремя итерациями цикла, оставив непрочитанным имя результирующего файла. -Считывание имен файлов производится в одну и ту же переменную s, поскольку после связывания файла, -имеющего имя s, -с соответствующей файловой переменной (процедурой Assign) все остальные действия -с данным файлом в нашей программе -будут осуществляться с использованием файловой переменной, без обращения к имени файла.

-

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

- -
- -
-

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

-
-

Ввод всех исходных данных без создания требуемого файла

-

Изменим программу, заменив в заголовке цикла число 3 на 4, и вновь запустим программу. Теперь все данные, -необходимые для выполнения задания, введены в программу (это видно по индикатору ввода). Однако задание не выполнено, поскольку результирующий файл -не создан. Поэтому решение опять признано ошибочным с диагностикой «Результирующий файл не найден»:

- -
- -
- -
-

Пример программы, приводящей к ошибке времени выполнения

-

Добавим в тело цикла после процедуры Assign вызов процедуры Reset, обеспечивающий открытие существующего файла:

-
-
-uses PT4;
-var
-  i: integer;
-  s: string;
-  f: array [1..4] of file of integer;
-begin
-  Task('File48');
-  for i := 1 to 4 do
-  begin
-    read(s);
-    Assign(f[i], s);
-    Reset(f[i]);
-  end;
-end.  
-
- -

Теперь запуск программы приведет к сообщению об ошибке «Error System.IO.FileNotFoundException»:

-
- -
- -

Сообщение, начинающееся со слова Error, означает, что при работе программы произошла ошибка времени выполнения -(Runtime Error). После слова Error указывается имя этой ошибки (в данном случае System.IO.FileNotFoundException, -то есть ошибка ввода-вывода, связанная с тем, что файл не найден) -и краткое ее описание на английском языке.

- -

Сообщение об ошибке времени выполнения появится и в разделе «Список ошибок» -окна PascalABC.NET:

-
- -
- -
-

Создание пустого результирующего файла

-

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

-
-
-uses PT4;
-var
-  i: integer;
-  s: string;
-  f: array [1..4] of file of integer;
-begin
-  Task('File48');
-  for i := 1 to 4 do
-  begin
-    read(s);
-    Assign(f[i], s);
-    if i < 4 then Reset(f[i])
-    else Rewrite(f[i]);
-  end;
-  { * }
-  for i := 1 to 4 do
-    Close(f[i]);
-end.  
-
- -

Комментарий { * } расположен в том месте программы, в котором можно выполнять операции ввода-вывода для всех -четырех файлов: они уже открыты процедурами Reset или Rewrite и еще не закрыты процедурой Close.

-

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

- -
- -
- -
-

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

-

Во всех ранее рассмотренных вариантах программы мы не использовали операции ввода-вывода для файлов. -Поэтому тип файлов не играл никакой роли: вместо типа file of integer мы могли использовать любой другой файловый тип, -например, file of real, и результат выполнения программы был бы тем же самым.

-

Тип файловых элементов становится принципиально важным, если в программе используются операции ввода-вывода -для данного файла. Чтобы продемонстрировать это на примере нашей программы, внесем в нее следующие изменения: -в описании массива f файловых переменных тип integer заменим на real, в раздел описаний добавим описание -переменной a типа real, в раздел операторов (в позицию, помеченную комментарием { * }) -добавим следующий фрагмент:

-
-
-for i := 1 to 3 do
-begin
-  read(f[i], a);
-  write(f[4], a);
-end;
-
-
-

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

-

Результат работы программы будет неожиданным:

- -
- -
- - -

Судя по экранной строке с содержимым результирующего файла, в него будут записаны не три, а шесть элементов, -по два начальных элемента из каждого исходного файла. Объясняется это тем, что после связывания файлов -с файловыми переменными типа file of real элементами файлов стали считаться вещественные числа -(занимающие в памяти по 8 байтов), тогда как «на самом деле», то есть по условию задания, элементами файлов -являются целые числа (занимающие в памяти по 4 байта). Поэтому считывание из файла и последующая запись в файл -одного «вещественного элемента» фактически приводит к считыванию и записи блока данных размером 8 байтов, -содержащего два последовательных целочисленных элемента исходного файла.

-

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

- - -
-

Исправление ошибки, связанной с неверными типами файловых данных

-

Заменим в нашей программе все описания real на integer:

-
-
-uses PT4;
-var
-  i: integer;
-  s: string;
-  f: array [1..4] of file of integer;
-  a: integer;
-begin
-  Task('File48');
-  for i := 1 to 4 do
-  begin
-    read(s);
-    Assign(f[i], s);
-    if i < 4 then Reset(f[i])
-    else Rewrite(f[i]);
-  end;
-  for i := 1 to 3 do
-  begin
-    read(f[i], a);
-    write(f[4], a);
-  end;
-  for i := 1 to 4 do
-    Close(f[i]);
-end.  
-
- - -

Мы получим все еще неверное, но вполне «понятное» решение: первые три элемента результирующего файла -совпадают с контрольными (то есть «правильными»), а прочие элементы отсутствуют:

- -
- -
- - -
-

Верное решение

-

Приведем, наконец, верное решение задания -File48:

-
-
-uses PT4;
-var
-  i, a: integer;
-  s: string;
-  f: array [1..4] of file of integer;
-begin
-  Task('File48');
-  for i := 1 to 4 do
-  begin
-    read(s);
-    Assign(f[i], s);
-    if i < 4 then Reset(f[i])
-    else Rewrite(f[i]);
-  end;
-  while not Eof(f[1]) do
-    for i := 1 to 3 do
-    begin
-      read(f[i], a);
-      write(f[4], a);
-    end;
-  for i := 1 to 4 do
-    Close(f[i]);
-end.  
-
- -

От предыдущего варианта данное решение отличается добавлением заголовка цикла while not Eof(f[1]) do, -который обеспечивает считывание всех элементов из исходных файлов (напомним, что по условию задания все исходные -файлы имеют одинаковый размер) и запись их в результирующий файл в нужном порядке. -После запуска этого варианта мы получим сообщение «Верное решение. Тест номер 1 (из 5)», -а после пяти подобных запусков — сообщение «Задание выполнено!»:

- -
- -
- - -
-

Просмотр результатов выполнения задания

-

Щелкнув мышью на метке «Результаты (F2)», расположенной в правом верхнем -углу окна задачника, или нажав клавишу F2, мы можем вывести на экран окно результатов, -в котором будет перечислены все наши попытки решения задачи:

-
-
-File48    a08/09 12:43 Ознакомительный запуск.
-File48    a08/09 12:50 Введены не все требуемые исходные данные.
-File48    a08/09 12:52 Результирующий файл не найден.
-File48    a08/09 12:53 Error System.IO.FileNotFoundException.
-File48    a08/09 12:57 Ошибочное решение.--3
-File48    a08/09 13:06 Задание выполнено!
-
-
- -

Для закрытия окна результатов достаточно нажать клавишу Esc. -Окно результатов можно отобразить на экране и после закрытия окна задачника и возврата в среду PascalABC.NET. -Для этого надо использовать команду меню «Модули | Просмотреть результаты», кнопку - или клавиатурную -комбинацию Shift+Ctrl+R. -

- - - - - diff --git a/PABCNetHelp/ForEducation/CheckedTasks/ctpointer.html b/PABCNetHelp/ForEducation/CheckedTasks/ctpointer.html deleted file mode 100644 index 82def7002..000000000 --- a/PABCNetHelp/ForEducation/CheckedTasks/ctpointer.html +++ /dev/null @@ -1,535 +0,0 @@ - - - - - - - - - -

Задания на указатели и динамические структуры данных

- -
- -

Пример 1. Анализ существующей динамической структуры

-
- -

В заданиях группы Dynamic мы встречаемся с двумя новыми видами данных: это динамические структуры, -реализованные в виде цепочек связанных друг -с другом записей типа TNode, и указатели типа PNode на записи TNode: -PNode = ^TNode. Типы TNode и PNode не являются стандартными типами языка Паскаль; -они определены в задачнике Programming Taskbook следующим образом (приводятся только те поля записи TNode, которые -используются при выполнении заданий группы Dynamic):

-
-
-type
-  PNode = ^TNode;
-  TNode = record
-    Data: integer;
-    Next: PNode;
-    Prev: PNode;
-    . . .
-  end;
-
-
-

На примере задания Dynamic2 рассмотрим особенности, связанные с -использованием этих новых типов данных.

- -
-

Создание программы-заготовки и знакомство с заданием

- -

Программа-заготовка для задания Dynamic2, созданная с помощью команды -меню «Модули | Создать шаблон программы», кнопки - или клавиатурной -комбинации Shift+Ctrl+L, имеет следующий вид:

- -
-
-uses PT4;
-
-begin
-  Task('Dynamic2');
-
-end.
-
-
- -

После запуска данной программы на экране появится окно задачника:

-
- -
- - - -

Это окно содержит в качестве исходных и результирующих данных новые элементы: динамические структуры и указатели.

- - -

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

- - -

Рассмотрим в качестве примера динамическую структуру, указанную на рисунке:

-
-
- P1
- 75 - 65 - 22 - 26 - 10 >nil
-
-
-

Этот текст означает, что структура состоит из 5 элементов, причем ее первый элемент имеет поле Data, -равное 75, и связан с помощью своего поля Next со вторым элементом, поле Data -которого равно 65, и так далее до последнего, пятого элемента, поле Data которого равно 10, -а поле Next равно nil, что является признаком завершения структуры. -Таким образом, текст, описывающий данную динамическую структуру, является максимально упрощенным вариантом -следующей схемы:

-
- -
- -

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

-

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

-

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

-
-
-P1 = ptr
-
-
-

Здесь текст P1 = является комментарием и выделяется, как обычный комментарий, -светло-серым цветом, а текст ptr означает, что этот элемент исходных данных является указателем, -который надо ввести в программу с помощью процедуры ввода read.

-

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

-

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

-

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

- -
-

Приступаем к решению

- -

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

-

Приведем вначале неполное решение задачи, выводящее все необходимые данные, кроме указателя на последний элемент:

-
-
-uses PT4;
-var
-  p1: PNode;
-  n: integer;
-begin
-  Task('Dynamic2');
-  read(p1);
-  n := 0;
-  while p1 <> nil do
-  begin
-    write(p1^.Data);
-    n := n + 1;
-    p1 := p1^.Next;
-  end;
-  write(n);
-end.
-
-
-

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

- -
- -
- -

Добавим в конец программы оператор

-
-
-write(p1);
-
-
-

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

- -
- -
- -
-

Правильное решение

- -

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

-
-uses PT4;
-var
-  p1,p2: PNode;
-  n: integer;
-begin
-  Task('Dynamic2');
-  read(p1);
-  n := 0;
-  while p1 <> nil do
-  begin
-    write(p1^.Data);
-    n := n + 1;
-    p2 := p1;       { сохраняем адрес текущего элемента }
-    p1 := p1^.Next; { и переходим к следующему элементу }
-  end;
-  write(n, p2);
-end.
-
- -

Запустив эту программу три раза, мы получим сообщение «Задание выполнено!»:

- -
- -
- -
-

Пример 2. Добавление элемента к динамической структуре

-
-

Знакомство с заданием

- - -

Рассмотрим простейшее задание, связанное с добавлением элемента к динамической структуре-стеку: -Dynamic3.

-

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

-
- -
-

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

-
-

Приступаем к решению

- - -

Что произойдет, если динамическая структура будет создана с ошибками? Для того чтобы это выяснить, вернем в программе, решающей задание -Dynamic3, указатель на прежнюю вершину стека, не добавляя к ней новый элемент:

-
-uses PT4;
-var
-  d: integer;
-  p1: PNode;
-begin
-  Task('Dynamic3');
-  read(d, p1);
-  write(p1);
-end.
-
-

После запуска данной программы окно задачника примет вид:

-
- -
-

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

-
-

Правильное решение

- - -

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

-
-uses PT4;
-var
-  d: integer;
-  p1, p2: PNode;
-begin
-  Task('Dynamic3');
-  read(d, p1);
-  New(p2);
-  p2^.Data := D;
-  p2^.Next := p1;
-  write(p2);
-end.
-
-

Приведем вид окна задачника при первом запуске этой программы:

-
- -
- -
-

Пример 3. Удаление элемента из динамической структуры

-
-

Знакомство с заданием

- - -

Рассмотрим простейшее задание на удаление элемента из динамической структуры — -Dynamic5. -В нем требуется удалить из стека вершину и вернуть указатель на новую вершину, то есть на элемент, -расположенный непосредственно за удаленным.

-

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

-

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

-
- -
-
-

Приступаем к решению

- - -

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

-
-uses PT4;
-var p1: PNode;
-begin
-  Task('Dynamic5');
-  read(p1);
-  write(p1^.Data, p1^.Next);
-end.
-
-

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

-
- -
-
-

Правильное решение

- - -

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

-
-uses PT4;
-var p1: PNode;
-begin
-  Task('Dynamic5');
-  read(p1);
-  write(p1^.Data, p1^.Next);
-  Dispose(p1);
-end.
-
-

Приведем вид окна задачника при первом запуске этой программы:

-
- -
- -
-

Пример 4. Двусвязные динамические структуры

-
-

Знакомство с заданием

-

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

-
- -
- - -

Динамическая структура, приведенная в разделе результатов, имеет две особенности: -во-первых, ее элементы связаны символом =, а во-вторых, перед первым элементом присутствует текст -nil<.

-

Это означает, что результирующая структура является двусвязной, то есть каждый ее элемент связан не -только с последующим элементом (с помощью поля Next, как в односвязной структуре), но и с -предыдущим элементом (с помощью нового поля Prev), а поле Prev первого элемента имеет -значение nil:

-
- -
- -
-

Приступаем к решению

- -

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

-
-uses PT4;
-var
-  p1, p: PNode;
-begin
-  Task('Dynamic30');
-  read(p1);
-  p := p1^.Next;
-  while p <> nil do
-  begin
-    p^.Prev := p1;
-    p1 := p1^.Next;
-    p := p^.Next;
-  end;
-  write(p1); { вывод указателя на последний элемент }
-end.
-
-

В этой программе мы определили поля Prev для всех элементов, кроме первого. Поэтому решение будет считаться ошибочным -(обратите внимание на то, что перед первым элементом полученного списка отсутствует текст nil<):

-
- -
-

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

-
-                         P2
-nil< 33 = 64 - 78 = 12 = 51 >nil
-
-

Это означает, что между вторым и третьим элементом структуры имеется не двойная, а одинарная связь -(поле Next второго элемента содержит адрес третьего элемента, а поле Prev третьего элемента -не содержит адрес второго).

-
-

Правильное решение

-

Для получения правильного решения достаточно добавить в программу перед циклом while -следующий оператор:

-
-  p1^.Prev := nil;
-
-

Приведем вид окна задачника при первом запуске исправленной программы:

-
- -
- - - -

Замечание. Для задания Dynamic30 возможен более короткий вариант решения, -в котором не требуется особо обрабатывать первый элемент списка: -

- -
-uses PT4;
-var
-  p1, p: PNode;
-begin
-  Task('Dynamic30');
-  p := nil;
-  read(p1);
-  while p1 <> nil do
-  begin
-    p1^.Prev := p;
-    p := p1;
-    p1 := p1^.Next;
-  end;
-  write(p);
-end.
-
- - - -
-

Пример 5. Циклические динамические структуры

-
-

Знакомство с заданием

-

Динамическая структура называется циклической, если она замкнута в «кольцо», то есть ее последний элемент -связан полем Next с первым (в случае двусвязной структуры требуется также, чтобы ее первый элемент -был связан полем Prev -с последним элементом). Простейшим заданием на циклические структуры является -Dynamic55, в котором требуется -преобразовать обычный двусвязный список -в циклический.

-

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

-
- -
- - -

Обозначения << = и = >> позволяют отличить циклический список от обычного -(напомним, что у обычного двусвязного списка поле Prev первого элемента и поле Next последнего -элемента равны nil).

-

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

-
- -
-
-

Приступаем к решению

-

Для решения задания Dynamic55 достаточно найти последний элемент исходного списка и связать его с -первым элементом:

-
-uses PT4;
-var
-  p1, p2: PNode;
-begin
-  Task('Dynamic55');
-  read(p1);
-  p2 := p1;
-  while p2^.Next <> nil do
-    p2 := p2^.Next;
-  p2^.Next := p1;
-  write(p2);
-end.
-
-

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

-
- -
- -
-

Правильное решение

-

Для получения правильного решения достаточно добавить в программу перед процедурой вывода write -следующий оператор:

-
-  p1^.Prev := p2;
-
-

Приведем вид окна задачника при первом запуске исправленной программы:

-
- -
- - - -
-

Просмотр результатов выполнения заданий

-

Щелкнув мышью на метке «Результаты (F2)», расположенной в правом верхнем -углу окна задачника, или нажав клавишу F2, мы можем вывести на экран окно результатов, -в котором будет перечислены все наши попытки решения задачи:

- -
-Dynamic2    a08/09 13:11 Ознакомительный запуск.
-Dynamic2    a08/09 13:15 Выведены не все результирующие данные.
-Dynamic2    a08/09 13:17 Ошибочное решение.
-Dynamic2    a08/09 13:20 Задание выполнено!
-Dynamic3    a08/09 13:21 Ознакомительный запуск.
-Dynamic3    a08/09 13:24 Ошибочное решение.
-Dynamic3    a08/09 13:28 Задание выполнено!
-Dynamic5    a08/09 13:29 Ознакомительный запуск.
-Dynamic5    a08/09 13:30 Не освобождена динамическая память.
-Dynamic5    a08/09 13:31 Задание выполнено!
-Dynamic30   a08/09 13:34 Ознакомительный запуск.
-Dynamic30   a08/09 13:42 Ошибочное решение.
-Dynamic30   a08/09 13:43 Задание выполнено!
-Dynamic55   a08/09 13:54 Ознакомительный запуск.
-Dynamic55   a08/09 13:57 Ошибочное решение.
-Dynamic55   a08/09 13:58 Задание выполнено!
-
-

Для закрытия окна результатов достаточно нажать клавишу Esc. -Окно результатов можно отобразить на экране и после закрытия окна задачника и возврата в среду PascalABC.NET. -Для этого надо использовать команду меню «Модули | Просмотреть результаты», кнопку - или клавиатурную -комбинацию Shift+Ctrl+R. -

- - - - diff --git a/PABCNetHelp/ForEducation/CheckedTasks/ctstring.html b/PABCNetHelp/ForEducation/CheckedTasks/ctstring.html deleted file mode 100644 index 90f375e63..000000000 --- a/PABCNetHelp/ForEducation/CheckedTasks/ctstring.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - - - - - - -

Задание на обработку строк

- -
-

Создание программы-заготовки и знакомство с заданием

- -

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

-

Программу-заготовку для решения этого задания можно создать с помощью -команды -меню «Модули | Создать шаблон программы», кнопки - или клавиатурной -комбинации Shift+Ctrl+L. Эта заготовка будет иметь следующий вид:

- -
-uses PT4;
-
-begin
-  Task('String9');
-
-end.
-
- -

После запуска данной программы на экране появится окно задачника:

- -
- -
- -

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


-

Ввод исходных данных

-

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

-
-
-uses PT4;
-var
-  n: integer;
-  c1, c2: char;
-begin
-  Task('String9');
-  read(c1, c2, n);
-end.
-
- - -

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

- -
- -
- -

Общее правило, определяющее порядок ввода и вывода данных для задачника Programming Taskbook -гласит: -ввод и вывод данных производится по строкам (слева направо), а строки просматриваются сверху вниз. -Иными словами, -данные, отображаемые в окне задачника, вводятся и выводятся в том порядке, в котором читается обычный -текст.

-

На панели индикаторов, расположенной под информационной панелью, -отмечено, что введен всего один элемент исходных данных (из трех), хотя в процедуре read нашей программы -были -указаны три переменные. Это объясняется тем, что при вводе первой из этих переменных (с1) -была обнаружена ошибка несоответствия типа, поэтому задачник не стал анализировать остальные данные, -которые программа пыталась ввести. Здесь проявляется еще одно правило задачника -Programming Taskbook: при обнаружении первой ошибки ввода-вывода -анализ оставшихся исходных данных и результатов не проводится. -

-

Исправим нашу программу, изменив порядкок параметров в процедуре ввода:

-
-
-  read(n, c1, c2);
-
-

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

-
- -
-

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

-
-

Формирование и вывод требуемой строки

-

Для формирования нужной строки воспользуемся операцией + (сцепления строк):

-
-
-uses PT4;
-var
-  n, i: integer;
-  c1, c2: char;
-  s: string;
-begin
-  Task('String9');
-  read(n, c1, c2);
-  s := '';
-  for i := 1 to n do
-    s := s + c1 + c2;
-  write(s);
-end.
-
-

Заметим, что в операторе s := '' нет необходимости, так как все глобальные переменные в Паскале автоматически -инициализируются «нулевыми» значениями (для числовых данных это обычные нули, для символов — это -символ #0, для -строк — пустая строка '').

-

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

-
- -
-

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

-
- -
-

Замечание. Красная звездочка может появиться и при выводе ошибочных числовых данных. Например, если ожидается -целое число в диапазоне от 1 до 99, а получено число 10000, то на экране изобразится первая цифра этого большого -числа, за которой будет указана красная звездочка: 1*.

-
-

Верное решение

-

Ошибка в предыдущей программе возникла из-за неверного указания количества итераций цикла. Действительно, -на каждой итерации к строке добавляется по два символа, поэтому после n итераций -строка будет содержать 2*n символов (а не n, как требуется в задании). -

Для исправления ошибки достаточно вдвое -уменьшить число итераций:

-
-
-uses PT4;
-var
-  n, i: integer;
-  c1, c2: char;
-  s: string;
-begin
-  Task('String9');
-  read(n, c1, c2);
-  s := '';
-  for i := 1 to n div 2 do
-    s := s + c1 + c2;
-  write(s);
-end.
-
-

После запуска этого варианта мы получим сообщение «Верное решение. Тест номер 1 (из 5)», -а после пяти подобных запусков — сообщение «Задание выполнено!»:

-
- -
-

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

-
-
-uses PT4;
-var
-  n, i: integer;
-  c1, c2: char;
-  s: string;
-begin
-  Task('String9');
-  read(n, c1, c2);
-  SetLength(s, n);
-  for i := 1 to n div 2 do
-  begin
-    s[2 * i - 1] := c1;
-    s[2 * i] := c2;
-  end;
-  write(s);
-end.
-
-

Обратите внимание на процедуру SetLength(s, n), которая обеспечивает правильную настройку длины результирующей -строки s. Без вызова этой процедуры программа работала бы неверно, так как любая -глобальная строковая переменная инициализируется пустой строкой, а при работе с отдельными символами строки -корректировка ее длины не производится.

- - - - -
-

Просмотр результатов выполнения задания

-

Щелкнув мышью на метке «Результаты (F2)», расположенной в правом верхнем -углу окна задачника, или нажав клавишу F2, мы можем вывести на экран окно результатов, -в котором будет перечислены все наши попытки решения задачи:

- -
-
-String9   a08/09 11:17 Ознакомительный запуск.
-String9   a08/09 11:18 Неверно указан тип при вводе исходных данных.
-String9   a08/09 11:21 Выведены не все результирующие данные.
-String9   a08/09 11:26 Ошибочное решение.
-String9   a08/09 11:29 Задание выполнено!
-
-
-

Для закрытия окна результатов достаточно нажать клавишу Esc. -Окно результатов можно отобразить на экране и после закрытия окна задачника и возврата в среду PascalABC.NET. -Для этого надо использовать команду меню «Модули | Просмотреть результаты», кнопку - или клавиатурную -комбинацию Shift+Ctrl+R. -

- - - - diff --git a/PABCNetHelp/ForEducation/CheckedTasks/cttree.html b/PABCNetHelp/ForEducation/CheckedTasks/cttree.html deleted file mode 100644 index af95d6ab6..000000000 --- a/PABCNetHelp/ForEducation/CheckedTasks/cttree.html +++ /dev/null @@ -1,314 +0,0 @@ - - - - - - - - - - - - -

Задания на обработку деревьев -

-
-

Пример 1. Анализ бинарного дерева -

-
-

В заданиях группы Tree, как и в заданиях группы Dynamic, мы встречаемся с двумя новыми видами данных: -это древовидные динамические структуры, реализованные в виде наборов связанных друг с -другом записей типа TNode, и указатели типа PNode на записи TNode: PNode = -^TNode. Типы TNode и PNode не являются стандартными типами языка Паскаль; они -определены в задачнике Programming Taskbook. -

-

Особенности, связанные с использованием новых типов данных, рассмотрим на -примере задания Tree2. -

-

Tree2°. -Дан адрес P1 записи типа TNode — корня дерева. -Эта запись связана полями Left и Right с другими записями того же типа (дочерними вершинами), -они, в свою очередь, — со своими дочерними вершинами, и так далее до записей, -поля Left и Right которых равны nil (у некоторых вершин может быть равно nil одно из полей Left или Right). -Вывести количество вершин дерева. -

-
-

Создание программы-заготовки и знакомство с заданием

-

Напомним, что программу-заготовку для решения этого задания можно создать с помощью -команды -меню «Модули | Создать шаблон программы», кнопки - или клавиатурной -комбинации Shift+Ctrl+L. Приведем текст созданной заготовки: -

- -
-uses PT4;
-
-begin
-  Task('Tree2');
-end.
-
-

После запуска программы на экране появится окно задачника: -

-
- -
- - -

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

-

Начнем с описания того, как отображается на экране дерево. Для его вывода используется -несколько экранных строк. На каждой строке изображаются вершины дерева, -находящиеся на определенном уровне (номер уровня указывается слева от изображения дерева). -Для каждой вершины выводится ее значение, т. е. значение поля Data соответствующей записи типа TNode. -Любая вершина соединяется линиями со своими дочерними вершинами, расположенными на следующем уровне дерева; -левая дочерняя вершина изображается слева от родительской вершины, а правая — справа. -Отсутствие у вершины одной или обеих дочерних вершин означает, что ее поля Left и/или Right равны nil. -

-

-Рассмотрим в качестве примера дерево, приведенное на рисунке. -Корень этого дерева имеет значение 15, левая дочерняя вершина корня равна 58, -правая дочерняя вершина равна 42, глубина дерева равна 4. Все листья дерева находятся на уровнях -3 и 4; листья на уровне 3 имеют значения 15 и 11, листья на уровне 4 — значения 38 и 84. -Некоторые из внутренних вершин дерева имеют по две -дочерние вершины (это корень и вершины со значениями 55 и 20), некоторые по одной: -левой (вершины 42, 87 и 60) или правой (вершина 58). -

-

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

-

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

- -

- -
-

Решение задачи

- -

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

-

Для выполнения этого задания, как и для подавляющего большинства других заданий на обработку деревьев, -следует воспользоваться вспомогательной рекурсивной подпрограммой (функцией или процедурой). -Рекурсивная природа алгоритмов, -связанных с обработкой деревьев (в частности, бинарных деревьев), объясняется тем, что сами -определения деревьев общего вида и бинарных деревьев являются рекурсивными. -Так, дать словесное описание функции NodeCount(P), подсчитывающей число вершин дерева с корнем, -с которым связан указатель P, можно следующим образом: если указатель P равен nil, -то следует вернуть значение 0; в противном случае следует вернуть значение -1 + NodeCount(P^.Left) + NodeCount(P^.Right) -(в этом выражении первое слагаемое соответствует корню дерева, второе — -его левому поддереву, а третье — его правому поддереву; при этом не требуется проверять, -что указанные поддеревья существуют, так как при их отсутствии соответствующее слагаемое просто будет равно нулю). -

-

Таким образом, решение задачи будет иметь следующий вид: -

-
-uses PT4;
-function NodeCount(P: PNode): integer;
-begin
-  if P = nil then
-    result := 0
-  else
-    result := 1 + NodeCount(P^.Left) + NodeCount(P^.Right);
-end;
-var P1: PNode;
-begin
-  Task('Tree2');
-  read(P1);
-  write(NodeCount(P1));
-end.
-
-

Цепочка рекурсивных вызовов функции NodeCount завершается при достижении терминальной вершины (листа), -у которой поля Left и Right равны nil. Благодаря наличию функции NodeCount, -раздел операторов программы является очень кратким: в нем считывается адрес P1 корня исходного дерева, -после чего вызывается функция NodeCount(P1), возвращаемое значение которой сразу выводится процедурой write. -

-

Запустив эту программу пять раз, мы получим сообщение «Задание -выполнено!». -

-
- -

Пример 2. Бинарные деревья с обратной связью -

-
-

Рассмотренная выше реализация бинарных деревьев позволяет легко переходить от родительских вершин -к их дочерним вершинам, но не допускает обратного перехода. В то же время, для некоторых задач, -связанных с обработкой деревьев, возможность обратного перехода от потомков к их предку позволяет -получить более простое решение. Ясно, что для обеспечения возможности обратного перехода -каждую вершину дерева надо снабдить еще одним полем связи, в котором должна храниться ссылка на -ее родительскую вершину. Это поле связи естественно назвать Parent. Поскольку корень дерева -предка не имеет, его поле Parent должно быть равно nil. -

-

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

-

Tree49°. -Дан указатель P1 на корень дерева, вершинами которого являются записи типа TNode, -связанные между собой с помощью полей Left и Right. Используя поле Parent записи TNode, -преобразовать исходное дерево в дерево с обратной связью, в котором каждая вершина -связана не только со своими дочерними вершинами (полями Left и Right), но и с родительской -вершиной (полем Parent). Поле Parent корня дерева положить равным nil. -

-

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

-
- -
- -

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

-

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

-
-uses PT4;
-procedure SetParent(P, Par: PNode);
-begin
-  if P = nil then
-    exit;
-  P^.Parent := Par;
-  SetParent(P^.Left, P);
-  SetParent(P^.Right, P);
-end;
-var P1: PNode;
-begin
-  Task('Tree49');
-  read(P1);
-  SetParent(P1, nil);
-end.
-
-

При стартовом запуске рекурсивной процедуры SetParent в качестве -второго параметра указывается nil. -

-

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

-
- -

Пример 3. Деревья общего вида -

-
-

С помощью связанных записей типа TNode можно моделировать не только бинарные деревья, -но и произвольные упорядоченные деревья, вершины которых имеют любое число непосредственных потомков -(будем называть такие деревья деревьями общего вида; для них также используется название -«деревья с множественным ветвлением»). -Рассмотрим задание Tree86 — первое из заданий, связанных с деревьями общего вида, в котором -описываются особенности подобных деревьев. -

-

Tree86°. -Дерево общего вида (каждая вершина которого может иметь произвольное число дочерних вершин, -расположенных в фиксированном порядке в направлении слева направо) реализуется с помощью набора -связанных записей типа TNode следующим образом: для каждой внутренней вершины ее поле Left -содержит указатель на ее первую (т. е. левую) дочернюю вершину, а поле Right — указатель на ее -правую сестру, т. е. вершину, имеющую в дереве общего вида того же родителя. -Поле Right корня дерева общего вида всегда равно nil, так как корень сестер не имеет. -Дан указатель P1 на корень непустого бинарного дерева. Создать дерево общего вида, -соответствующее исходному бинарному дереву, и вывести указатель P2 на его корень. -

-

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

-
- -
- -

Корень этого дерева (со значением 13) имеет три дочерние вершины (71, 73 и 29), -причем вершина 71 не имеет потомков, вершина 73 имеет три непосредственных потомка (18, 93 и 92), -а вершина 29 — два (24 и 84). На последнем уровне располагается вершина 46, являющаяся -единственной дочерней вершиной вершины 93. -

-

При ознакомительном запуске задания Tree86 на экране появится окно, подобное следующему. -

-
- -
-

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

-

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

-

При формировании нового дерева будем использовать рекурсивную функцию CreateNode(P). -Параметр P содержит указатель на вершину исходного дерева, копия которой создается при вызове функции. -Возвращаемым значением функции является указатель на созданную вершину (как обычно, если P = nil, -то функция не выполняет никаких действий и возвращает nil). Для создания дочерних вершин выполняется -рекурсивный вызов этой функции. Заметим, что цепочка дочерних вершин может быть пустой -(если вершина P является листом), содержать один элемент (если вершина P имеет только одного -непосредственного потомка) или два элемента. Перед формированием цепочки дочерних вершин удобно -занести адреса дочерних вершин вершины P во вспомогательные переменные P1 и P2. При этом в случае, -если вершина P имеет только одного потомка (неважно, левого или правого), адрес этого потомка -заносится в переменную P1, а переменная P2 остается равной nil. Благодаря использованию -переменных P1 и P2, фрагмент кода, отвечающий за формирование списка дочерних вершин, -удается сделать более кратким. Приведем текст программы, решающей задачу Tree86. -

-
-uses PT4;
-function CreateNode(P: PNode): PNode;
-var P1, P2: PNode;
-begin
-  if P = nil then
-  begin
-    result := nil;
-    exit;
-  end;
-  New(result);
-  result^.Data := P^.Data;
-  result^.Right := nil;
-  P1 := P^.Left;
-  P2 := P^.Right;
-  if P1 = nil then
-  begin
-    P1 := P2;
-    P2 := nil;
-  end;
-  { формирование списка дочерних вершин }
-  result^.Left := CreateNode(P1);
-  if P1 <> nil then
-    result^.Left^.Right := CreateNode(P2);
-end;
-var P1: PNode;
-begin
-  Task('Tree86');
-  read(P1);
-  write(CreateNode(P1));
-end.
-
-

-Примечание. Фрагмент дерева общего вида, содержащий все дочерние вершины некоторой вершины, -можно рассматривать как односвязный список, элементы которого связаны между собой -с помощью поля Right (у последнего элемента списка поле Right равно nil). -Каждый элемент подобного списка может содержать «подсписок» своих дочерних элементов; -адрес начала этого подсписка хранится в поле Left данного элемента. -Поэтому в алгоритмах, связанных с обработкой вершин деревьев общего вида, -для перебора непосредственных потомков некоторой вершины удобно использовать цикл -(как при переборе элементов списка), в то время как для обработки каждой дочерней вершины следует, -как обычно, использовать рекурсию. -

- - diff --git a/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for array of T.html b/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for array of T.html index 154188deb..cb3eb4f0d 100644 --- a/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for array of T.html +++ b/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for array of T.html @@ -35,6 +35,7 @@ + @@ -187,6 +188,8 @@
        Сортирует массив по убыванию по ключу function Swap<T>(Self: array of T; i, j: integer): array of T;
        Меняет местами элементы массива с индексами i и j +function ToString(Self: array of real; digits: integer): string; +
        Возвращает строковое представление массива вещественных чисел с фиксированным количеством знаков после десятичной точки function Transform<T>(Self: array of T; f: T->T): array of T;
        Преобразует элементы массива по заданному правилу function Transform<T>(Self: array of T; f: (T,integer)->T): array of T; diff --git a/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for sequence of T.html b/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for sequence of T.html index 5ad86d22b..6e4b3f8f0 100644 --- a/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for sequence of T.html +++ b/PABCNetHelp/LangGuide/PABCSystemUnit/Files/Extension methods for sequence of T.html @@ -191,6 +191,8 @@
        Возвращает произведение элементов последовательности function Product(Self: sequence of integer): int64;
        Возвращает произведение элементов последовательности +function Product(Self: sequence of int64): int64; +
        Возвращает произведение элементов последовательности function Product<T>(Self: sequence of T; f: T->real): real;
        Возвращает произведение элементов последовательности, спроектированных на числовое значение function Product<T>(Self: sequence of T; f: T->integer): int64; diff --git a/Release/pabcversion.txt b/Release/pabcversion.txt index 0cc1d75df..1ce7a306c 100644 --- a/Release/pabcversion.txt +++ b/Release/pabcversion.txt @@ -1 +1 @@ -3.11.0.3699 +3.11.0.3700 diff --git a/ReleaseGenerators/PascalABCNET_version.nsh b/ReleaseGenerators/PascalABCNET_version.nsh index 6827aa8c4..488b08325 100644 --- a/ReleaseGenerators/PascalABCNET_version.nsh +++ b/ReleaseGenerators/PascalABCNET_version.nsh @@ -1 +1 @@ -!define VERSION '3.11.0.3699' +!define VERSION '3.11.0.3700' diff --git a/TestSuite/CompilationSamples/PABCSystem.pas b/TestSuite/CompilationSamples/PABCSystem.pas index 750a1d88b..c3b25efca 100644 --- a/TestSuite/CompilationSamples/PABCSystem.pas +++ b/TestSuite/CompilationSamples/PABCSystem.pas @@ -11274,6 +11274,14 @@ begin Result *= x; end; +/// Возвращает произведение элементов последовательности +function Product(Self: sequence of int64): int64; extensionmethod; +begin + Result := 1; + foreach var x in Self do + Result *= x; +end; + /// Возвращает произведение элементов последовательности, спроектированных на числовое значение function Product(Self: sequence of T; f: T->real): real; extensionmethod; begin @@ -13903,6 +13911,12 @@ begin yield i; end; +/// Возвращает строковое представление массива вещественных чисел с фиксированным количеством знаков после десятичной точки +function ToString(Self: array of real; digits: integer): string; extensionmethod; +begin + Result := Self.Select(x -> Round(x,digits)).JoinToString; +end; + function NextCombHelper(ind: array of integer; m,n: integer): boolean; begin for var i:=m-1 downto 0 do diff --git a/bin/Lib/PABCSystem.pas b/bin/Lib/PABCSystem.pas index b0a62fae4..c3b25efca 100644 --- a/bin/Lib/PABCSystem.pas +++ b/bin/Lib/PABCSystem.pas @@ -11274,6 +11274,14 @@ begin Result *= x; end; +/// Возвращает произведение элементов последовательности +function Product(Self: sequence of int64): int64; extensionmethod; +begin + Result := 1; + foreach var x in Self do + Result *= x; +end; + /// Возвращает произведение элементов последовательности, спроектированных на числовое значение function Product(Self: sequence of T; f: T->real): real; extensionmethod; begin diff --git a/bin/PascalABCNET.chm b/bin/PascalABCNET.chm index 750ae1a03..1aec87f65 100644 Binary files a/bin/PascalABCNET.chm and b/bin/PascalABCNET.chm differ