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 @@ - - - - -![]()
Группа заданий 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 можно вводить и выводить элементы данных несколькими способами, с использованием переменных
-различных типов; например, исходную строку можно либо сразу прочесть в строковую переменную, либо вводить
-посимвольно в цикле (в других группах заданий проводится более строгая проверка на соответствие типа переменной
-типу элемента исходных или результирующих данных).
-
Рассмотрим еще одно задание группы 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 «более приближены» к реальной экзаменационной ситуации, -в которой программа учащегося должна не только обрабатывать исходные данные, но и обеспечивать наглядное -отображение результатов. -
- -Группа 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). -
-При первом тестовом испытании программы ей будет предложен для обработки -набор данных не слишком большого размера (порядка 1020 элементов). -
-Вначале следует определиться со структурами данных, которые будут использоваться в программе. Поскольку
-требуется найти одну характеристику для каждого года, а число лет невелико, можно использовать
-числовой массив 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 тестовых испытаний программы, требуемых для того, чтобы решение было зачтено как выполненное, -будут пройдены успешно. -
-Завершая рассмотрение этого задания, опишем некоторые дополнительные возможности, связанные -с просмотром больших наборов данных. -
-Начиная со второго испытания, -программе может быть предложен для обработки набор исходных данных большего размера (порядка 50100 -элементов). При этом уже не удастся отобразить на экране все данные, связанные с заданием. -В подобной ситуации у правой границы окна задачника появится полоса прокрутки, позволяющая -перемещаться к той части данных, которая первоначально не отображается на экране. -Прокрутку данных можно выполнять не только с помощью полосы прокрутки, но и используя -клавиши со стрелками, PgUp, PgDn, Home, End, а также колесико мыши. -
-Помимо стандартных действий по прокрутке данных, в окне задачника предусмотрены возможности -«интеллектуальной» прокрутки, -позволяющие быстро перейти к началу каждого раздела задания, а также сравнить соответствующие фрагменты -полученных результатов и примера верного решения. Для циклического перебора разделов сверху вниз -предназначена клавиша [+] (а также комбинация Ctrl+PgDn), для циклического перебора разделов снизу вверх - клавиша [] (а также комбинация Ctrl+PgUp). Для быстрого переключения между -соответствующими фрагментами разделов с результатами и с примером верного решения предназначена клавиша [/] -(а также комбинация Ctrl+Tab). Все эти действия можно выполнить и с помощью мыши; для этого -предусмотрены кнопки в левом верхнем углу прокручиваемой области окна, отведенной под отображение разделов задания -(эти кнопки отображаются -на экране, если размер данных, связанных с заданием, превышает размеры окна). -Приведем вид окна задачника с полосой прокрутки и дополнительными кнопками: -
----
Обозначения на кнопках совпадают с клавишами, -выполняющими те же действия; при наведении мышью на кнопку рядом с ней появляется всплывающая подсказка. -
-Кроме трех кнопок, связанных с «интеллектуальной» прокруткой, -в приведенном на рисунке окне отображаются еще две дополнительные кнопки. -Первая из них располагается в правом верхнем углу раздела с формулировкой -и позволяет временно скрыть (а в дальнейшем опять отобразить) -раздел с формулировкой (эти же действия можно выполнить с помощью клавиши Del или щелчка мышью -на разделе с формулировкой). -Вторая дополнительная кнопка расположена в правом верхнем углу раздела с исходными данными. -Как уже отмечалось ранее, эта кнопка позволяет переключаться между полным -и сокращенным отображением наборов данных. Напомним, что -изображение на этой кнопке показывает текущий режим отображения данных. Например, если на кнопке изображена -стилизованная стрелка, направленная вверх (как на приведенном выше рисунке), -значит, в данный момент в окне отображаются все данные, а нажатие на эту кнопку переведет окно -в режим отображения нескольких начальных (как правило, пяти) элементов каждого набора данных. -
-Дополнительная информация о возможностях -режима окна с динамической компоновкой приведена в соответствующем разделе -страницы, посвященной описанию окна задачника. -
- -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.
-
В заданиях группы 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.
-
-
-Запустив эту программу три раза, мы получим сообщение «Задание выполнено!»:
- --- --
Рассмотрим простейшее задание, связанное с добавлением элемента к динамической структуре-стеку: -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.
-
-Приведем вид окна задачника при первом запуске этой программы:
--- --
Рассмотрим простейшее задание на удаление элемента из динамической структуры -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.
-
-Приведем вид окна задачника при первом запуске этой программы:
--- --
Особенности работы с двусвязными динамическими структурами рассмотрим на примере задания -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.
-
-
-
-
-Динамическая структура называется циклической, если она замкнута в «кольцо», то есть ее последний элемент
-связан полем 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.
-
В качестве примера задания на обработку строк рассмотрим задание 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.
-
В заданиях группы 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.
-
Запустив эту программу пять раз, мы получим сообщение «Задание -выполнено!». -
-Рассмотренная выше реализация бинарных деревьев позволяет легко переходить от родительских вершин
-к их дочерним вершинам, но не допускает обратного перехода. В то же время, для некоторых задач,
-связанных с обработкой деревьев, возможность обратного перехода от потомков к их предку позволяет
-получить более простое решение. Ясно, что для обеспечения возможности обратного перехода
-каждую вершину дерева надо снабдить еще одним полем связи, в котором должна храниться ссылка на
-ее родительскую вершину. Это поле связи естественно назвать 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).
-
С помощью связанных записей типа 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 данного элемента.
-Поэтому в алгоритмах, связанных с обработкой вершин деревьев общего вида,
-для перебора непосредственных потомков некоторой вершины удобно использовать цикл
-(как при переборе элементов списка), в то время как для обработки каждой дочерней вершины следует,
-как обычно, использовать рекурсию.
-
function Swap<T>(Self: array of T; i, j: integer): array of T;
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