product для int64
This commit is contained in:
parent
16a8066a5b
commit
6d14df7ffc
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%MINOR%=11
|
||||
%REVISION%=3699
|
||||
%COREVERSION%=0
|
||||
%REVISION%=3700
|
||||
%MINOR%=11
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
Приведем формулировку этого задания.
|
||||
</p>
|
||||
|
||||
<p><b>a1</b>. Закрасить помеченные клетки.</p>
|
||||
<p><b>RBa1</b>. Закрасить помеченные клетки.</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/RBa1-1.png">
|
||||
</blockquote>
|
||||
|
|
|
|||
|
|
@ -1,654 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
|
||||
<title></title>
|
||||
<link rel="StyleSheet" href="../../default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a name="main"></a>
|
||||
|
||||
<h1>Задания, связанные с ЕГЭ по информатике
|
||||
</h1>
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<h2>Пример 1. Простая задача на реализацию базовых алгоритмов
|
||||
</h2>
|
||||
<hr>
|
||||
<p>Группа заданий ExamBegin посвящена базовым алгоритмическим задачам,
|
||||
включенным в кодификатор ЕГЭ по информатике. Процесс выполнения подобных заданий мы рассмотрим на примере
|
||||
одной из простых задач, связанных с нахождением максимумов и минимумов из двух, трех или четырех чисел
|
||||
без использования массивов и циклов.
|
||||
</p>
|
||||
<p class="task">ExamBegin2°. На вход подаются три вещественных числа; числа расположены в одной строке.
|
||||
Вывести вначале минимальное, а затем максимальное из них. Каждое число должно выводиться на новой строке
|
||||
и снабжаться комментарием: «MIN=» для минимального, «MAX=»
|
||||
для максимального.
|
||||
</p>
|
||||
<hr>
|
||||
<h3>Создание программы-заготовки и знакомство с заданием
|
||||
</h3>
|
||||
<p>Напомним, что программу-заготовку для решения этого задания можно создать с помощью
|
||||
команды
|
||||
меню «Модули | Создать шаблон программы», кнопки
|
||||
<img border="0" src="gif/Load.gif" width="16" height="17"> или клавиатурной
|
||||
комбинации <b>Shift+Ctrl+L</b>. Приведем текст созданной заготовки:
|
||||
</p>
|
||||
<blockquote><code><b>uses</b> PT4Exam;
|
||||
|
||||
<b><br><br>begin</b>
|
||||
<br>
|
||||
Task('ExamBegin2');
|
||||
|
||||
<b><br>end</b>.
|
||||
</code></blockquote>
|
||||
<p>После запуска программы на экране появится <a href="../PT4/window.html">окно задачника</a>:
|
||||
</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamBegin2-1.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Обсудим особенности программы-заготовки и окна задачника.
|
||||
</p>
|
||||
<p>В программе-заготовке вместо модуля <code>PT4</code>
|
||||
подключается модуль <code>PT4Exam</code>, специально предназначенный для использования при выполнении
|
||||
заданий групп Exam. Данный модуль содержит реализацию единственной процедуры <code>Task</code>,
|
||||
инициализирующей задание. Никакие дополнительные процедуры, связанные с вводом-выводом, в него не включены.
|
||||
Это обусловлено тем, что ввод-вывод при выполнении заданий групп Exam надо выполнять, используя
|
||||
<i>стандартные процедуры языка Pascal</i>.
|
||||
</p>
|
||||
<p>Основной особенностью окна задачника является то, что в разделе исходных данных отсутствуют данные,
|
||||
выделенные желтым цветом (напомним, что желтый цвет используется для выделения данных, которые необходимо
|
||||
вводить с помощью специальных процедур ввода задачника). Вместо этого в окне отображается
|
||||
строка бирюзового цвета, содержащая числовые данные. Вид строки подчеркивает то обстоятельство,
|
||||
что вводить данные требуется не с помощью специальных процедур ввода, имеющихся в задачнике,
|
||||
а с помощью стандартных процедур языка Pascal. Отметим, что бирюзовый цвет используется в окне задачника
|
||||
для отображения «внешних» данных (содержащихся в файлах или динамических структурах), доступ к которым
|
||||
должен осуществляться с помощью стандартных средств используемого языка программирования.
|
||||
</p>
|
||||
<p>Пример верного решения выделяется серым цветом
|
||||
(в отличие от «настоящих» результатов, выведенных программой учащегося, которые, как и входные данные,
|
||||
выделяются бирюзовым цветом), однако <i>представление</i> выходных данных совпадает с
|
||||
представлением входных: это набор строк,
|
||||
содержащих числовые данные (дополненные комментариями). Вид данных в разделе результатов показывает,
|
||||
что для их вывода, как и для ввода исходных данных, необходимо использовать стандартные процедуры языка Pascal.
|
||||
</p>
|
||||
<p>
|
||||
<b>Примечание.</b>
|
||||
Если вы уже выполняли задания, связанные с обработкой файлов, то можете заметить, что отображение данных
|
||||
в заданиях групп Exam
|
||||
в точности соответствует способу отображения содержимого <i>текстовых файлов</i>. Это совпадение не случайно.
|
||||
На самом деле во всех заданиях групп Exam все исходные данные хранятся в специальном входном текстовом файле,
|
||||
а все результаты должны записываться в специальный выходной текстовый файл. Однако при этом не требуется
|
||||
выполнять особых действий, связанных с определением имен этих файлов, связыванием файлов с файловыми
|
||||
переменными, открытием и закрытием файлов (все эти действия выполняются задачником автоматически).
|
||||
Для программы, выполняющей задание, эти файлы играют роль <i>стандартных потоков ввода-вывода</i>,
|
||||
поэтому для доступа к ним достаточно использовать обычные процедуры ввода-вывода языка Pascal.
|
||||
</p>
|
||||
<hr>
|
||||
<h3>Ввод исходных данных и их обработка
|
||||
</h3>
|
||||
<p>Приступим к выполнению задания. В данном случае следует использовать алгоритм, не требующий
|
||||
применения массивов, поэтому опишем три простые переменные вещественного типа и введем в них исходные данные:
|
||||
</p>
|
||||
<blockquote><code><b>uses</b> PT4Exam;
|
||||
<b><br><br>begin</b> <br>
|
||||
Task('ExamBegin2');
|
||||
<br>
|
||||
<b>var</b>
|
||||
(a,b,c) := ReadReal3;
|
||||
<b><br>end</b>.
|
||||
</code></blockquote>
|
||||
<p>Мы воспользовались стандартной функцией ввода <code>ReadReal3</code>, введя все три исходных числа за один ее вызов.
|
||||
</p>
|
||||
<p>При запуске приведенного выше варианта программы вид окна не изменится, поскольку мы не вывели никаких данных.
|
||||
В заданиях групп Exam запуск программы считается ознакомительным до тех пор, пока программа не выведет
|
||||
хотя бы один элемент результирующих данных. Кроме того, задачник не контролирует,
|
||||
каким образом программа читает исходные данные (например, мы могли бы ввести всю исходную строку
|
||||
в переменную типа <code>string</code>, а затем «разобрать» эту строку, выделив из нее три числа
|
||||
и преобразовав их к типу <code>real</code>). Отмеченные особенности характерны именно для заданий групп Exam,
|
||||
в которых для ввода данных не используются специальные средства задачника.
|
||||
</p>
|
||||
<p>Реализуем алгоритм нахождения минимального и максимального элемента. Для этого опишем еще две
|
||||
переменные <code>min</code> и <code>max</code> типа <code>real</code> и добавим в конец программы следующие операторы:
|
||||
</p>
|
||||
<blockquote><code><b>if</b> a < b <b>then</b>
|
||||
<b><br>begin</b>
|
||||
<br>
|
||||
min := a;
|
||||
<br> max := b;
|
||||
<b><br>end</b>
|
||||
<b><br>else</b>
|
||||
<b><br>begin</b> <br>
|
||||
min := b;
|
||||
<br> max := a;
|
||||
<b><br>end</b>;
|
||||
<b><br>if</b> c < min <b>then</b> <br>
|
||||
min := c
|
||||
<b><br>else</b>
|
||||
<b>if</b> c > max <b>then</b> <br>
|
||||
max := c;
|
||||
|
||||
</code></blockquote>
|
||||
<p>Таким образом, для одновременного нахождения минимального и максимального из трех чисел требуется
|
||||
не более трех операций сравнения и не более трех операций присваивания.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<h3>Вывод результатов и их форматирование
|
||||
</h3>
|
||||
<p>Осталось вывести полученные результаты. Вывод, как и ввод, следует выполнять с помощью стандартных процедур
|
||||
языка Pascal, учитывая их особенности.
|
||||
</p>
|
||||
<p>Вначале, в качестве примера, организуем вывод, не соответствующий условиям задачи. Для этого добавим в конец программы
|
||||
следующий оператор:
|
||||
</p>
|
||||
<blockquote><code>Println(min, max); </code></blockquote>
|
||||
<p>Приведем вид окна задачника при запуске полученной программы:
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamBegin2-2.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Мы видим, что минимум и максимум найдены правильно, однако выведены не в том формате,
|
||||
который требовался (требуемый формат приводится в разделе с примером верного решения).
|
||||
Мы допустили при выводе три ошибки: во-первых, вывели оба числа в одной строке
|
||||
(при этом они «слились», поскольку мы не предусмотрели вывод пробела-разделителя), во-вторых,
|
||||
не снабдили числа комментариями и, в-третьих, не настроили их отображение в формате
|
||||
с двумя дробными знаками (первое число было выведено с единственным знаком после точки).
|
||||
</p>
|
||||
<p>
|
||||
<b>Примечание.</b> Следует обратить внимание на панель индикаторов, которая отображается между информационной панелью
|
||||
и разделом с формулировкой задания в случае, если запуск программы не является
|
||||
ни демонстрационным, ни ознакомительным. Обычно на этой панели выводятся три индикатора: первый указывает
|
||||
количество введенных исходных данных, второй — количество выведенных результатов, а третий —
|
||||
количество успешно пройденных тестовых испытаний. При выполнении заданий, связанных с ЕГЭ, первые два
|
||||
индикатора являются неактивными, поскольку, как было отмечено выше, для получения исходных данных
|
||||
и записи результатов не используются средства задачника, и поэтому он не в состоянии
|
||||
проконтролировать каждую операцию ввода-вывода.
|
||||
</p>
|
||||
<p>Для исправления первых двух ошибок достаточно изменить вывод следующим образом:
|
||||
</p>
|
||||
<blockquote><code>Println('MIN=', min); <br>Println('MAX=', max); </code></blockquote>
|
||||
<p>Однако в этом случае числа по-прежнему могут содержать неверное число дробных знаков.
|
||||
Для исправления этой последней ошибки проще всего использовать
|
||||
<i>атрибуты форматирования</i>, начинающиеся с символа «:» (двоеточие):
|
||||
</p>
|
||||
<blockquote><code>Println('MIN=', min:0:2); <br>Println('MAX=', max:0:2); </code></blockquote>
|
||||
<p>Первый атрибут определяет <i>ширину поля вывода</i> (если ширина равна 0, то используется минимально
|
||||
необходимое поле вывода). Наличие второго атрибута (допустимого только для вещественных чисел) означает,
|
||||
что число надо вывести в формате с фиксированной точкой, причем его значение равно количеству дробных знаков.
|
||||
</p>
|
||||
<p>При запуске исправленной программы будет выведено сообщение о верном решении, а после трех
|
||||
запусков — сообщение о том, что задание выполнено:
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamBegin2-3.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>В случае успешного прохождения тестового испытания в окно задачника не включается
|
||||
раздел с примером верного решения, поскольку данные этого раздела совпадают
|
||||
с результатами, полученными программой.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Примечание.</b>
|
||||
В системе PascalABC.NET,
|
||||
благодаря специальному механизму перенаправления данных, стандартные процедуры <code>read-write</code> можно использовать
|
||||
и при выполнении заданий из других групп, однако важно учитывать ряд особенностей использования этих процедур
|
||||
в заданиях групп Exam. Во-первых, только в заданиях групп Exam будут проявляться различия в использовании
|
||||
процедур <code>read-write</code> и <code>readln-writeln</code> (в остальных группах для ввода-вывода можно использовать как вариант
|
||||
процедуры с суффиксом «ln», так и вариант без этого суффикса). Во-вторых, только в заданиях
|
||||
групп Exam можно при необходимости <i>использовать атрибуты форматирования</i> при выводе результатов,
|
||||
а также <i>выводить дополнительные комментарии</i>, если этого требует условие задачи. В-третьих,
|
||||
только в заданиях групп Exam можно вводить и выводить элементы данных несколькими способами, с использованием переменных
|
||||
различных типов; например, исходную строку можно либо сразу прочесть в строковую переменную, либо вводить
|
||||
посимвольно в цикле (в других группах заданий проводится более строгая проверка на соответствие типа переменной
|
||||
типу элемента исходных или результирующих данных).
|
||||
</p>
|
||||
|
||||
|
||||
<hr>
|
||||
<h2>Пример 2. Ввод и вывод массивов
|
||||
</h2>
|
||||
<hr>
|
||||
|
||||
<p>Рассмотрим еще одно задание группы ExamBegin, особенностью которого является вывод в качестве результата
|
||||
элементов двумерного массива.
|
||||
</p>
|
||||
|
||||
<p class="task">ExamBegin28°. На вход в первой строке подаются два целых положительных числа <i>M</i> и <i>N</i>,
|
||||
во второй строке — вещественное число <i>D</i>, а в третьей строке — набор из <i>M</i> вещественных чисел.
|
||||
Сформировать и вывести двумерный вещественный массив размера <i>M</i> × <i>N</i>, у которого первый
|
||||
столбец совпадает с исходным набором чисел, а элементы каждого следующего столбца равны сумме соответствующего
|
||||
элемента предыдущего столбца и числа <i>D</i> (в результате каждая строка массива будет содержать
|
||||
элементы <i>арифметической прогрессии</i>). Каждую строку элементов массива выводить на новой экранной строке,
|
||||
для каждого числа отводить 7 экранных позиций.
|
||||
</p>
|
||||
|
||||
|
||||
<p>При запуске программы-заготовки, созданной для этого задания, окно задачника примет следующий вид:
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamBegin28-1.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Анализируя исходные данные, можно заметить, что полученная матрица должна иметь 8 строк, тогда как
|
||||
на экране отображаются только первые пять.
|
||||
Это связано с тем, что по умолчанию используется режим «свернутого» отображения данных, при котором
|
||||
на экране выводится только несколько начальных строк. Признаком того, что имеются данные,
|
||||
не выведенные на экране, является кнопка, которая отображается в правом верхнем углу раздела исходных данных
|
||||
(на этой кнопке изображается стилизованная стрелка, направленная вниз).
|
||||
Для вывода всех данных достаточно нажать эту кнопку; можно также нажать клавишу <b>Ins</b> или выполнить
|
||||
щелчок мышью в любом месте раздела с данными задания (кроме раздела, содержащего формулировку).
|
||||
Если выполнить эти действия для нашего окна, то оно изменится следующим образом:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamBegin28-2.png">
|
||||
</blockquote>
|
||||
<p>Повторный щелчок мышью, нажатие клавиши <b>Ins</b> или кнопки (на которой в данной ситуации будет отображаться
|
||||
стрелка, направленная вверх — см. рисунок) восстанавливает «сокращенное»
|
||||
отображение данных. Заметим, что в режиме «сокращенного» отображения нумеруется только первая строка данных,
|
||||
а в режиме полного отображения нумерацией снабжаются все строки.</p>
|
||||
|
||||
<p>
|
||||
<a href="#scroll">Дополнительные возможности</a>, связанные с просмотром
|
||||
данных большого размера, будут описаны далее, в пункте, посвященном задачам повышенной сложности.
|
||||
</p>
|
||||
|
||||
<p>Если закрыть окно задачника, находясь в режиме отображения всех данных,
|
||||
то при последующих запусках программы окно будет сразу отображаться в этом режиме.
|
||||
</p>
|
||||
|
||||
<p>Приведем вариант правильного решения данной задачи (в этом варианте учитывается, что результирующий
|
||||
двумерный массив имеет не более 10 строк и столбцов; соответствующее условие приведено в
|
||||
преамбуле к описанию
|
||||
группы ExamBegin):
|
||||
</p>
|
||||
|
||||
<blockquote><code><pre><b>uses</b> PT4Exam;
|
||||
<b>var</b>
|
||||
m, n, i, j: integer;
|
||||
d: real;
|
||||
a: array[1..10, 1..10] of real;
|
||||
<b>begin</b>
|
||||
Task('ExamBegin28');
|
||||
readln(m, n, d);
|
||||
<b>for</b> i := 1 <b>to</b> m <b>do</b>
|
||||
read(a[i, 1]);
|
||||
<b>for</b> j := 2 <b>to</b> n <b>do</b>
|
||||
<b>for</b> i := 1 <b>to</b> m <b>do</b>
|
||||
a[i, j] := a[i, j - 1] + d;
|
||||
<b>for</b> i := 1 <b>to</b> m <b>do</b>
|
||||
<b>begin</b>
|
||||
<b>for</b> j := 1 <b>to</b> n <b>do</b>
|
||||
write(a[i, j]:7:2);
|
||||
writeln;
|
||||
<b>end</b>;
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
<p>В приведенном решении следует обратить особое внимание на организацию ввода-вывода.
|
||||
Укажем две особенности, связанные с вводом. Во-первых, несмотря
|
||||
на то что по условию число <code>d</code> находится во второй строке, мы смогли включить его в один список с
|
||||
предшествующими числами <code>m</code> и <code>n</code> (поскольку при чтении числовых данных переход на новую строку выполняется
|
||||
автоматически). Во-вторых, при чтении элементов исходного набора <i>необходимо</i> использовать вариант
|
||||
процедуры <code>read</code> без суффикса «ln», чтобы не пропустить оставшиеся в этой строке числа.
|
||||
</p>
|
||||
<p>При выводе полученного двумерного массива необходимо обеспечить его правильное <i>форматирование</i>:
|
||||
каждый элемент должен выводиться на семи экранных позициях с двумя дробными знаками и, кроме того,
|
||||
каждая строка массива должна выводиться на новой экранной строке. Это достигается за счет использования
|
||||
соответствующих атрибутов форматирования и явного перехода на новую строку с помощью процедуры
|
||||
<code>writeln</code> без параметров.
|
||||
</p>
|
||||
<p>
|
||||
<b>Примечание.</b>
|
||||
Заметим, что в заданиях группы Matrix,
|
||||
также посвященной обработке двумерных массивов, специальные действия по форматированию полученных массивов
|
||||
выполнять не требуется, так как задачник автоматически форматирует все полученные результаты.
|
||||
Таким образом, задания группы ExamBegin «более приближены» к реальной экзаменационной ситуации,
|
||||
в которой программа учащегося должна не только обрабатывать исходные данные, но и обеспечивать наглядное
|
||||
отображение результатов.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<a name="taskc"></a>
|
||||
<h2>Пример 3. Обработка сложных наборов данных
|
||||
</h2>
|
||||
<hr>
|
||||
|
||||
<p>Группа ExamTaskC содержит 100 типовых заданий, аналогичных заданиям,
|
||||
которые предлагаются на ЕГЭ по информатике
|
||||
в качестве задач повышенной сложности (задача C4). Основную часть данной группы составляют
|
||||
задания на обработку сложных наборов данных (записей) с элементами-полями различных типов.
|
||||
В подобных заданиях требуется правильно выбрать способ хранения данных и организовать их эффективную обработку;
|
||||
при этом обычно требуется применить <i>несколько</i> базовых алгоритмов, например, алгоритм суммирования
|
||||
или нахождения минимума/максимума и алгоритм поиска нужного элемента или сортировки набора данных по требуемому ключу.
|
||||
В группу ExamTaskC включены также задания повышенной сложности на обработку текстовых данных
|
||||
(подобные задания содержатся в завершающем разделе данной группы).
|
||||
</p>
|
||||
<p>Следует заметить, что возможность автоматической генерации
|
||||
больших наборов исходных данных, предоставляемая задачником Programming Taskbook, позволяет существенно ускорить
|
||||
тестирование учебных программ и сделать его более надежным, что, в свою очередь, повышает эффективность изучения
|
||||
типовых приемов решения задач группы C.
|
||||
</p>
|
||||
<p>В заданиях группы ExamTaskC ввод и вывод имеет те же особенности,
|
||||
что и в заданиях группы ExamBegin.
|
||||
</p>
|
||||
<p>Рассмотрим следующее задание.
|
||||
</p>
|
||||
|
||||
<p class="task">ExamTaskC25°. На вход подаются сведения об абитуриентах. В первой строке
|
||||
указывается количество абитуриентов <i>N</i>, каждая из последующих <i>N</i> строк имеет
|
||||
формат<br> <tt><Номер школы> <Год поступления> <Фамилия></tt><br>
|
||||
Номер школы содержит не более двух цифр, годы лежат в диапазоне от 1990 до 2010. Для каждого года,
|
||||
присутствующего в исходных данных, вывести общее число абитуриентов, поступивших в этом году
|
||||
(вначале выводить год, затем число абитуриентов). Сведения о каждом годе выводить на новой строке
|
||||
и упорядочивать по возрастанию номера года.</p>
|
||||
|
||||
|
||||
<p>Программа-заготовка, созданная для этого задания, подобно заготовкам для заданий группы ExamBegin,
|
||||
будет использовать специальный модуль <code>PT4Exam</code>:
|
||||
</p>
|
||||
|
||||
<blockquote><code><pre><b>uses</b> PT4Exam;
|
||||
|
||||
<b>begin</b>
|
||||
Task('ExamTaskC25');
|
||||
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
<p>При запуске этой программы на экране появится окно задачника, содержащее следующие данные:
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamTaskC25-1.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Окно будет иметь такой вид, если при его предшествующем закрытии оно находилось в режиме
|
||||
отображения всех данных.
|
||||
Для отображения всех данных на экране может потребоваться увеличить высоту окна; для этого достаточно
|
||||
зацепить мышью заголовок окна и переместить его вверх (для перемещения заголовка окна задачника вверх и вниз
|
||||
можно также воспользоваться клавиатурными комбинациями <b>Ctrl+Up</b> и <b>Ctrl+Down</b>).
|
||||
</p>
|
||||
<p>При первом тестовом испытании программы ей будет предложен для обработки
|
||||
набор данных не слишком большого размера (порядка 10–20 элементов).
|
||||
</p>
|
||||
<p>Вначале следует определиться со структурами данных, которые будут использоваться в программе. Поскольку
|
||||
требуется найти одну характеристику для каждого года, а число лет невелико, можно использовать
|
||||
числовой массив <code>year</code>, каждый элемент которого соответствует определенному году. Так как в языке Pascal можно
|
||||
использовать произвольные границы индексов, удобно в качестве диапазона индексов указать диапазон лет,
|
||||
который требуется проанализировать: <code>1990..2010</code>. В начале программы выполним инициализацию элементов массива,
|
||||
положив их значения равными <code>0</code>
|
||||
(заметим, что если после обработки исходных данных некоторые элементы массива <code>year</code>
|
||||
останутся нулевыми, то это будет означать, что соответствующие годы не были представлены в наборе исходных данных,
|
||||
и выводить информацию о них не следует).
|
||||
</p>
|
||||
|
||||
<p>После инициализации массива следует прочесть информацию о количестве абитуриентов и организовать цикл,
|
||||
в котором будут обрабатываться данные о каждом абитуриенте и соответствующим образом корректироваться
|
||||
элементы массива <code>year</code>. В дальнейшем сведения об уже обработанном абитуриенте нам не будут нужны, поэтому сохранять
|
||||
их в специальном наборе данных (например, массиве) не требуется. Заметим также, что фамилия абитуриента
|
||||
для решения задачи не требуется, поэтому после чтения двух числовых данных можно сразу переходить к новой строке,
|
||||
пропуская строковый элемент данных (фамилию). В задаче не нужно использовать
|
||||
и номера школ, однако их придется считывать, так как только после номера школы указывается интересующий
|
||||
нас год поступления абитуриента.
|
||||
</p>
|
||||
|
||||
<p>Когда данные обо всех абитуриентах будут обработаны,
|
||||
в массиве <code>year</code> будет содержаться вся необходимая информация, которую останется
|
||||
вывести в формате, указанном в условии задачи.
|
||||
</p>
|
||||
|
||||
<p>Приведем первый вариант решения (этот вариант содержит одну ошибку):
|
||||
</p>
|
||||
|
||||
<blockquote><code><pre><b>uses</b> PT4Exam;
|
||||
<b>var</b>
|
||||
n, i, k, m: integer;
|
||||
year: array[1990..2010] of integer;
|
||||
<b>begin</b>
|
||||
Task('ExamTaskC25');
|
||||
<b>for</b> i := 1990 <b>to</b> 2010 <b>do</b>
|
||||
year[i] := 0;
|
||||
readln(n);
|
||||
<b>for</b> i := 1 <b>to</b> n <b>do</b>
|
||||
<b>begin</b>
|
||||
readln(k, m); <i>{ k - номер школы, m - год поступления }</i>
|
||||
Inc(year[m]);
|
||||
<b>end</b>;
|
||||
<b>for</b> i := 1990 <b>to</b> 2010 <b>do</b>
|
||||
writeln(i, ' ', year[i]);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
<p>Ошибка связана с тем, что на экран выводится информация о годах, отсутствующих в наборе исходных данных.
|
||||
Поэтому она сразу будет выявлена при обработке наборов данных небольшого размера,
|
||||
предлагаемых программе при первом тестовом запуске
|
||||
(для большей наглядности приведем окно задачника в режиме сокращенного отображения данных, при котором
|
||||
выводятся только пять первых элементов из каждого набора данных):
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamTaskC25-2.png">
|
||||
</blockquote>
|
||||
<p>Для исправления ошибки достаточно добавить в последний цикл условный оператор:
|
||||
</p>
|
||||
|
||||
<blockquote><code><pre><b>for</b> i := 1990 <b>to</b> 2010 <b>do</b>
|
||||
<b>if</b> year[i] > 0 <b>then</b>
|
||||
writeln(i, ' ', year[i]);
|
||||
</pre></code></blockquote>
|
||||
|
||||
|
||||
<p>Теперь все 9 тестовых испытаний программы, требуемых для того, чтобы решение было зачтено как выполненное,
|
||||
будут пройдены успешно.
|
||||
</p>
|
||||
<a name="scroll"></a><p>Завершая рассмотрение этого задания, опишем некоторые дополнительные возможности, связанные
|
||||
с просмотром больших наборов данных.
|
||||
</p>
|
||||
<p>Начиная со второго испытания,
|
||||
программе может быть предложен для обработки набор исходных данных большего размера (порядка 50–100
|
||||
элементов). При этом уже не удастся отобразить на экране все данные, связанные с заданием.
|
||||
В подобной ситуации у правой границы окна задачника появится <i>полоса прокрутки</i>, позволяющая
|
||||
перемещаться к той части данных, которая первоначально не отображается на экране.
|
||||
Прокрутку данных можно выполнять не только с помощью полосы прокрутки, но и используя
|
||||
клавиши со стрелками, <b>PgUp</b>, <b>PgDn</b>, <b>Home</b>, <b>End</b>, а также колесико мыши.
|
||||
</p>
|
||||
<p>Помимо стандартных действий по прокрутке данных, в окне задачника предусмотрены возможности
|
||||
«интеллектуальной» прокрутки,
|
||||
позволяющие быстро перейти к началу каждого раздела задания, а также сравнить соответствующие фрагменты
|
||||
полученных результатов и примера верного решения. Для циклического перебора разделов сверху вниз
|
||||
предназначена клавиша <b>[+]</b> (а также комбинация <b>Ctrl+PgDn</b>), для циклического перебора разделов снизу вверх
|
||||
— клавиша <b>[–]</b> (а также комбинация <b>Ctrl+PgUp</b>). Для быстрого переключения между
|
||||
соответствующими фрагментами разделов с результатами и с примером верного решения предназначена клавиша <b>[/]</b>
|
||||
(а также комбинация <b>Ctrl+Tab</b>). Все эти действия можно выполнить и с помощью мыши; для этого
|
||||
предусмотрены кнопки в левом верхнем углу прокручиваемой области окна, отведенной под отображение разделов задания
|
||||
(эти кнопки отображаются
|
||||
на экране, если размер данных, связанных с заданием, превышает размеры окна).
|
||||
Приведем вид окна задачника с полосой прокрутки и дополнительными кнопками:
|
||||
</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamTaskC25-3.png">
|
||||
</blockquote>
|
||||
<p>Обозначения на кнопках совпадают с клавишами,
|
||||
выполняющими те же действия; при наведении мышью на кнопку рядом с ней появляется всплывающая подсказка.
|
||||
</p>
|
||||
<p>Кроме трех кнопок, связанных с «интеллектуальной» прокруткой,
|
||||
в приведенном на рисунке окне отображаются еще две дополнительные кнопки.
|
||||
Первая из них располагается в правом верхнем углу раздела с формулировкой
|
||||
и позволяет временно скрыть (а в дальнейшем опять отобразить)
|
||||
раздел с формулировкой (эти же действия можно выполнить с помощью клавиши <b>Del</b> или щелчка мышью
|
||||
на разделе с формулировкой).
|
||||
Вторая дополнительная кнопка расположена в правом верхнем углу раздела с исходными данными.
|
||||
Как уже отмечалось ранее, эта кнопка позволяет переключаться между полным
|
||||
и сокращенным отображением наборов данных. Напомним, что
|
||||
изображение на этой кнопке показывает текущий режим отображения данных. Например, если на кнопке изображена
|
||||
стилизованная стрелка, направленная вверх (как на приведенном выше рисунке),
|
||||
значит, в данный момент в окне отображаются все данные, а нажатие на эту кнопку переведет окно
|
||||
в режим отображения нескольких начальных (как правило, пяти) элементов каждого набора данных.
|
||||
</p>
|
||||
<p>Дополнительная информация о возможностях
|
||||
режима окна с динамической компоновкой приведена в <a href="../PT4/window.html#dynmode">соответствующем разделе</a>
|
||||
страницы, посвященной описанию окна задачника.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<h2>Пример 4. Более сложное задание на обработку наборов данных
|
||||
</h2>
|
||||
<hr>
|
||||
|
||||
<p class="task">ExamTaskC53°. На вход подаются сведения о ценах на бензин на автозаправочных станциях (АЗС).
|
||||
В первой строке содержится значение <i>M</i> одной из марок бензина, во второй строке указывается
|
||||
целое число <i>N</i>, а каждая из последующих <i>N</i> строк имеет формат<br>
|
||||
<tt><Марка бензина> <Улица> <Компания> <Цена 1 литра (в копейках)></tt><br>
|
||||
Имеется не более 20 различных компаний и не более 30 различных улиц; названия компаний и улиц
|
||||
не содержат пробелов. В качестве марки бензина указываются числа 92, 95 или 98. Цена задается целым числом
|
||||
в диапазоне от 2000 до 3000. Каждая компания имеет не более одной АЗС на каждой улице;
|
||||
цены на разных АЗС одной и той же компании могут различаться. Для каждой улицы, на которой имеются
|
||||
АЗС с бензином марки <i>M</i>, определить максимальную цену бензина этой марки (вначале выводить максимальную
|
||||
цену, затем название улицы). Сведения о каждой улице выводить на новой строке и упорядочивать
|
||||
по возрастанию максимальной цены, а для одинаковой цены — по названиям улиц в алфавитном порядке.
|
||||
Если ни одной АЗС с бензином марки <i>M</i> не найдено, то вывести текст «Нет».
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<p>Приведем окно задачника, которое появится на экране при запуске программы-заготовки для данного задания
|
||||
(в данном окне скрыт раздел с формулировкой; в результате оказались скрытыми и кнопки,
|
||||
отвечающие за «интеллектуальную» прокрутку, поскольку в окне полностью отображается
|
||||
содержимое оставшихся разделов):
|
||||
</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/ExamTaskC53-1.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Выясним, какая структура является наиболее подходящей для хранения информации, необходимой для решения задачи.
|
||||
Нам требуется информация, связанная с различными улицами, которых по условию не более 30, причем для каждой улицы
|
||||
надо хранить сведения двух видов: ее название и максимальную цену бензина марки <i>M</i>. Поэтому мы можем либо завести
|
||||
массив из 30 элементов-записей с двумя полями, либо два массива: один содержащий названия улиц, а другой —
|
||||
максимальные цены. Учитывая, что в конце программы нам потребуется выполнять сортировку полученных данных,
|
||||
целесообразнее использовать массив записей, поскольку это позволит записать алгоритм сортировки
|
||||
в более компактной форме.
|
||||
</p>
|
||||
|
||||
<p>Определим запись <code>Street</code> с двумя полями <code>name</code> и <code>max</code> и опишем массив <code>s</code>
|
||||
из 30 элементов типа <code>Street</code>.
|
||||
Следует также завести переменную <code>ns</code>, в которой будет храниться количество заполненных элементов массива <code>s</code>.
|
||||
</p>
|
||||
|
||||
<p>При обработке каждой строки с исходными данными нам будут нужны прежде всего сведения о марке бензина.
|
||||
Если марка бензина не равна <code>M</code>, то оставшуюся часть строки обрабатывать не требуется, и можно сразу перейти
|
||||
к разбору следующей строки. Если марка бензина равна <code>M</code>, то необходимо узнать название улицы <code>s0</code> и цену бензина <code>p</code>.
|
||||
Заметим, что название компании для решения задачи не требуется, однако его необходимо прочесть, чтобы определить
|
||||
следующий элемент данных — цену бензина.
|
||||
</p>
|
||||
|
||||
<p>Если улица с названием <code>s0</code> еще не была включена в массив <code>s</code>, то ее необходимо включить в массив, присвоив
|
||||
полю <code>max</code> значение <code>p</code>. Если же улица уже присутствует в массиве, то необходимо сравнить поле <code>max</code> для данной улицы
|
||||
и значение <code>p</code>, изменив при необходимости поле <code>max</code> (здесь мы используем базовый алгоритм нахождения
|
||||
максимального значения).
|
||||
</p>
|
||||
|
||||
<p>Для ввода названий улиц и компаний в нашем случае удобно организовать <i>посимвольное</i> чтение строковых данных;
|
||||
признаком завершения такого чтения будет обнаружение пробельного символа.
|
||||
</p>
|
||||
|
||||
<p>После обработки набора исходных данных необходимо проверить, найдена ли хотя бы одна улица с АЗС,
|
||||
предлагающей марку бензина <code>M</code> (для этого достаточно сравнить значение <code>ns</code> с нулем). Если ни одна улица
|
||||
не найдена, то надо вывести строку «Нет»; в противном случае требуется выполнить сортировку
|
||||
массива <code>s</code> по указанному набору ключей и вывести полученные данные в требуемом порядке. Поскольку размер
|
||||
массива невелик, для его сортировки вполне допустимо использовать один из простых алгоритмов,
|
||||
например, алгоритм <i>пузырьковой сортировки</i>.
|
||||
</p>
|
||||
|
||||
<p>Приведем один из вариантов правильного решения задачи:
|
||||
</p>
|
||||
|
||||
<blockquote><code><pre><b>uses</b> PT4Exam;
|
||||
<b>type</b>
|
||||
Street = <b>record</b>
|
||||
name: string;
|
||||
max: integer;
|
||||
<b>end</b>;
|
||||
<b>var</b>
|
||||
m, n, ns, i, j, k, p: integer;
|
||||
s: array[1..30] of Street;
|
||||
s0: string;
|
||||
x: Street;
|
||||
c: char;
|
||||
<b>begin</b>
|
||||
Task('ExamTaskC53');
|
||||
readln(m); <i>{ m - марка бензина }</i>
|
||||
readln(n);
|
||||
ns := 0;
|
||||
<b>for</b> i := 1 <b>to</b> n <b>do</b>
|
||||
<b>begin</b>
|
||||
read(k);
|
||||
<b>if</b> k <> m <b>then</b>
|
||||
readln <i>{ пропускаем оставшуюся часть строки }</i>
|
||||
<b>else</b>
|
||||
<b>begin</b>
|
||||
s0 := '';
|
||||
read(c); <i>{ пропускаем пробел после первого числа }</i>
|
||||
read(c); <i>{ читаем первый символ названия улицы }</i>
|
||||
<b>while</b> c <> ' ' <b>do</b>
|
||||
<b>begin</b>
|
||||
s0 := s0 + c;
|
||||
read(c);
|
||||
<b>end</b>;
|
||||
read(c); <i>{ читаем первый символ названия компании }</i>
|
||||
<b>while</b> c <> ' ' <b>do</b>
|
||||
read(c); <i>{ название компании не сохраняем }</i>
|
||||
readln(p); <i>{ читаем цену бензина и переходим на новую строку }</i>
|
||||
<i>{ Обработка прочитанной информации }</i>
|
||||
k := 0;
|
||||
<b>for</b> j := 1 <b>to</b> ns <b>do</b>
|
||||
<b>if</b> s[j].name = s0 <b>then</b> <i>{ улица уже содержится в массиве s }</i>
|
||||
<b>begin</b>
|
||||
k := 1;
|
||||
<b>if</b> s[j].max < p <b>then</b>
|
||||
s[j].max := p;
|
||||
break;
|
||||
<b>end</b>;
|
||||
<b>if</b> k = 0 <b>then</b> <i>{ улица еще не содержится в массиве s }</i>
|
||||
<b>begin</b>
|
||||
Inc(ns);
|
||||
s[ns].name := s0;
|
||||
s[ns].max := p;
|
||||
<b>end</b>;
|
||||
<b>end</b>;
|
||||
<b>end</b>;
|
||||
<b>if</b> ns = 0 <b>then</b> <i>{ ни одной улицы не найдено }</i>
|
||||
writeln('Нет')
|
||||
<b>else</b>
|
||||
<b>begin</b>
|
||||
<i>{ Сортировка по возрастанию максимальной цены,</i>
|
||||
<i> а для одинаковых цен - по названиям улиц }</i>
|
||||
<b>for</b> k := 1 <b>to</b> ns - 1 <b>do</b>
|
||||
<b>for</b> i := 1 <b>to</b> ns - k <b>do</b>
|
||||
<b>if</b> (s[i].max > s[i + 1].max) <b>or</b>
|
||||
(s[i].max = s[i + 1].max) <b>and</b>
|
||||
(s[i].name > s[i + 1].name) <b>then</b>
|
||||
<b>begin</b>
|
||||
x := s[i];
|
||||
s[i] := s[i + 1];
|
||||
s[i + 1] := x;
|
||||
<b>end</b>;
|
||||
<i>{ Вывод результатов в требуемом порядке }</i>
|
||||
<b>for</b> i := 1 <b>to</b> ns <b>do</b>
|
||||
writeln(s[i].max,' ',s[i].name);
|
||||
<b>end</b>;
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
|
||||
|
|
@ -1,343 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
|
||||
<title></title>
|
||||
<link rel="StyleSheet" href="../../default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<H1>Задание на обработку файлов</H1>
|
||||
|
||||
<hr>
|
||||
<h3>Создание программы-заготовки и знакомство с заданием</h3>
|
||||
|
||||
<p>В качестве примера задания на обработку файлов рассмотрим задание File48.</p>
|
||||
<p>Напомним, что программу-заготовку для решения этого задания можно создать с помощью
|
||||
команды
|
||||
меню «Модули | Создать шаблон программы», кнопки
|
||||
<img border="0" src="gif/Load.gif" width="16" height="17"> или клавиатурной
|
||||
комбинации <b>Shift+Ctrl+L</b>. Эта заготовка будет иметь следующий вид:
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
|
||||
<b>begin</b>
|
||||
Task('File48');
|
||||
|
||||
<b>end</b>.</pre></code>
|
||||
</blockquote>
|
||||
|
||||
<p>После запуска данной программы на экране появится <a href="../PT4/window.html">окно задачника</a>:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-1.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>В первой строке раздела исходных данных указаны имена трех исходных файлов (S<sub>A</sub>, S<sub>B</sub>
|
||||
и S<sub>C</sub>) и одного результирующего (S<sub>D</sub>). В последующих строках раздела исходных данных
|
||||
показано содержимое исходных файлов.
|
||||
Элементы файлов отображаются бирюзовым цветом, чтобы подчеркнуть их отличие от обычных исходных данных (желтого цвета)
|
||||
и комментариев (светло-серого цвета).</p>
|
||||
<p>Поскольку размер файлов, как правило, превышает количество элементов,
|
||||
которое может уместиться на одной экранной строке,
|
||||
для отображения содержимого файла может отводиться
|
||||
более одной экранной строки. Слева от каждой строки с содержимым файла указывается порядковый номер
|
||||
файлового элемента, значение которого указано первым в этой строке (элементы нумеруются от 1).</p>
|
||||
<p>Запуск нашей программы признан <i>ознакомительным</i> (и поэтому правильность решения не анализировалась),
|
||||
так как в ходе ее выполнения не было введено ни одного элемента исходных данных.
|
||||
При ознакомительном запуске раздел результатов не отображается, однако приводится
|
||||
<i>пример верного решения</i>, т. е. те числа,
|
||||
которые должны содержаться в результирующем файле при правильной обработке исходных файлов.</p>
|
||||
</p>
|
||||
|
||||
|
||||
<hr>
|
||||
<h3>Ввод части исходных данных</h3>
|
||||
<p>Добавим в программу фрагмент, позволяющий ввести имена исходных файлов и связать с этими файлами соответствующие
|
||||
файловые переменные. Поскольку мы собираемся работать с четырьмя файлами одного типа, удобно предусмотреть <i>массив</i>
|
||||
для хранения всех файловых переменных:</p>
|
||||
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
i: integer;
|
||||
s: string;
|
||||
f: <b>array </b>[1..4] <b>of</b> <b>file</b> <b>of</b> integer;
|
||||
<b>begin</b>
|
||||
Task('File48');
|
||||
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(s);
|
||||
Assign(f[i], s);
|
||||
<b>end</b>;
|
||||
<b>end</b>. </pre></code>
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Мы намеренно ограничились <i>тремя</i> итерациями цикла, оставив непрочитанным имя результирующего файла.
|
||||
Считывание имен файлов производится в одну и ту же переменную <code>s</code>, поскольку после связывания файла,
|
||||
имеющего имя <code>s</code>,
|
||||
с соответствующей файловой переменной (процедурой <code>Assign</code>) все остальные действия
|
||||
с данным файлом в нашей программе
|
||||
будут осуществляться с использованием файловой переменной, без обращения к имени файла.</p>
|
||||
<p>Запуск нового варианта программы уже не будет считаться ознакомительным, поскольку в программе
|
||||
выполняется ввод исходных данных. Так как имя результирующего файла осталось непрочитанным, этот вариант
|
||||
решения будет признан неверным и приведет к сообщению «<i>Введены не все требуемые исходные данные</i>»:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-2.png">
|
||||
</blockquote>
|
||||
<p>При этом на экране появится раздел результатов (кроме комментария он пока ничего не содержит),
|
||||
а также панель индикаторов. Первый из индикаторов
|
||||
(индикатор ввода) показывает
|
||||
количество введенных исходных данных. Обратите внимание на то, что второй индикатор
|
||||
(индикатор вывода) является неактивным: он выделяется серым цветом более светлого
|
||||
оттенка и не содержит текстового заголовка.
|
||||
Это объясняется тем, что индикатор вывода показывает количество результирующих данных,
|
||||
полученных задачником от программы, а в нашем случае программа
|
||||
не должна передавать задачнику никакие данные; вместо этого ей необходимо создать файл и заполнить его
|
||||
требуемыми значениями. Для заданий подобного типа (обычно это задания, связанные с обработкой файлов)
|
||||
индикатор вывода не используется.
|
||||
</p>
|
||||
<hr>
|
||||
<h3>Ввод всех исходных данных без создания требуемого файла</h3>
|
||||
<p>Изменим программу, заменив в заголовке цикла число <code>3</code> на <code>4</code>, и вновь запустим программу. Теперь все данные,
|
||||
необходимые для выполнения задания, введены в программу (это видно по индикатору ввода). Однако задание не выполнено, поскольку результирующий файл
|
||||
не создан. Поэтому решение опять признано ошибочным с диагностикой «<i>Результирующий файл не найден</i>»:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-3.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<h3>Пример программы, приводящей к ошибке времени выполнения</h3>
|
||||
<p>Добавим в тело цикла после процедуры <code>Assign</code> вызов процедуры <code>Reset</code>, обеспечивающий открытие существующего файла:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
i: integer;
|
||||
s: string;
|
||||
f: <b>array </b>[1..4] <b>of</b> <b>file</b> <b>of</b> integer;
|
||||
<b>begin</b>
|
||||
Task('File48');
|
||||
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(s);
|
||||
Assign(f[i], s);
|
||||
Reset(f[i]);
|
||||
<b>end</b>;
|
||||
<b>end</b>. </pre></code>
|
||||
</blockquote>
|
||||
|
||||
<p>Теперь запуск программы приведет к сообщению об ошибке «<i>Error System.IO.FileNotFoundException</i>»:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-4.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Сообщение, начинающееся со слова <i>Error</i>, означает, что при работе программы произошла <i>ошибка времени выполнения</i>
|
||||
(Runtime Error). После слова <i>Error</i> указывается имя этой ошибки (в данном случае <i>System.IO.FileNotFoundException</i>,
|
||||
то есть ошибка ввода-вывода, связанная с тем, что файл не найден)
|
||||
и краткое ее описание на английском языке.</p>
|
||||
|
||||
<p>Сообщение об ошибке времени выполнения появится и в разделе «Список ошибок»
|
||||
окна <b>PascalABC.NET</b>:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-5.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<h3>Создание пустого результирующего файла</h3>
|
||||
<p>Для того чтобы избежать ошибки времени выполнения, отсутствующий файл результатов следует открыть
|
||||
не процедурой <code>Reset</code>, а процедурой <code>Rewrite</code>, которая
|
||||
и обеспечит создание этого файла. Далее, после завершения работы с файлами, открытыми в программе,
|
||||
их необходимо закрыть процедурой <code>Close</code>. Добавим в программу соответствующие операторы:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
i: integer;
|
||||
s: string;
|
||||
f: <b>array </b>[1..4] <b>of file of</b> integer;
|
||||
<b>begin</b>
|
||||
Task('File48');
|
||||
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(s);
|
||||
Assign(f[i], s);
|
||||
<b>if</b> i < 4 <b>then</b> Reset(f[i])
|
||||
<b>else</b> Rewrite(f[i]);
|
||||
<b>end</b>;
|
||||
{ * }
|
||||
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
|
||||
Close(f[i]);
|
||||
<b>end</b>. </pre></code>
|
||||
</blockquote>
|
||||
|
||||
<p>Комментарий <code>{ * }</code> расположен в том месте программы, в котором можно выполнять операции ввода-вывода для всех
|
||||
четырех файлов: они уже открыты процедурами <code>Reset</code> или <code>Rewrite</code> и еще не закрыты процедурой <code>Close</code>.</p>
|
||||
<p>Запуск этого варианта программы не приведет к ошибке времени выполнения; более того, результирующий файл будет создан.
|
||||
Однако созданный файл останется пустым, то есть не содержащим ни одного элемента. Поэтому при запуске программы
|
||||
на информационной панели появится сообщение «<i>Ошибочное решение</i>»,
|
||||
а в строке, которая должна содержать элементы результирующего файла, появится текст <code>EOF:</code> (особое значение EOF для
|
||||
указателя текущей файловой позиции означает, что данный файл существует, но не содержит ни одного элемента):</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-6.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<h3>Пример программы, использующей неправильные типы для файловых данных</h3>
|
||||
<p>Во всех ранее рассмотренных вариантах программы мы не использовали операции ввода-вывода для файлов.
|
||||
Поэтому тип файлов не играл никакой роли: вместо типа <code><b>file of</b> integer</code> мы могли использовать любой другой файловый тип,
|
||||
например, <code><b>file of</b> real</code>, и результат выполнения программы был бы тем же самым.</p>
|
||||
<p>Тип файловых элементов становится принципиально важным, если в программе используются операции ввода-вывода
|
||||
для данного файла. Чтобы продемонстрировать это на примере нашей программы, внесем в нее следующие изменения:
|
||||
в описании массива <code>f</code> файловых переменных тип <code>integer</code> заменим на <code>real</code>, в раздел описаний добавим описание
|
||||
переменной <code>a</code> типа <code>real</code>, в раздел операторов (в позицию, помеченную комментарием <code>{ * }</code>)
|
||||
добавим следующий фрагмент:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(f[i], a);
|
||||
write(f[4], a);
|
||||
<b>end</b>;
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
<p>Данный фрагмент обеспечивает считывание <i>одного</i> элемента для каждого из трех исходных файлов
|
||||
и запись этих элементов в результирующий файл (в требуемом порядке). Подчеркнем, что мы <i>неправильно</i> указали
|
||||
типы файлов; тем не менее, компиляция программы пройдет успешно, а после ее запуска не произойдет
|
||||
ошибок времени выполнения.</p>
|
||||
<p>Результат работы программы будет неожиданным:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-7.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Судя по экранной строке с содержимым результирующего файла, в него будут записаны не три, а <i>шесть элементов</i>,
|
||||
по два начальных элемента из каждого исходного файла. Объясняется это тем, что после связывания файлов
|
||||
с файловыми переменными типа <code><b>file of</b> real</code> элементами файлов стали считаться <i>вещественные числа</i>
|
||||
(занимающие в памяти по 8 байтов), тогда как «на самом деле», то есть по условию задания, элементами файлов
|
||||
являются целые числа (занимающие в памяти по 4 байта). Поэтому считывание из файла и последующая запись в файл
|
||||
одного «вещественного элемента» фактически приводит к считыванию и записи блока данных размером 8 байтов,
|
||||
содержащего <i>два</i> последовательных целочисленных элемента исходного файла.</p>
|
||||
<p>Итак, мы выяснили, что ошибки, связанные с несоответствием типов файлов, не выявляются при компиляции
|
||||
и не всегда приводят к ошибкам времени выполнения. Это следует иметь в виду, и при появлении «странных»
|
||||
результирующих данных начинать поиск ошибки с проверки типов файловых переменных.</p>
|
||||
|
||||
|
||||
<hr>
|
||||
<h3>Исправление ошибки, связанной с неверными типами файловых данных</h3>
|
||||
<p>Заменим в нашей программе все описания <code>real</code> на <code>integer</code>:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
i: integer;
|
||||
s: string;
|
||||
f: <b>array </b>[1..4] <b>of file of</b> integer;
|
||||
a: integer;
|
||||
<b>begin</b>
|
||||
Task('File48');
|
||||
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(s);
|
||||
Assign(f[i], s);
|
||||
<b>if</b> i < 4 <b>then</b> Reset(f[i])
|
||||
<b>else</b> Rewrite(f[i]);
|
||||
<b>end</b>;
|
||||
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(f[i], a);
|
||||
write(f[4], a);
|
||||
<b>end</b>;
|
||||
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
|
||||
Close(f[i]);
|
||||
<b>end</b>. </pre></code>
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Мы получим все еще неверное, но вполне «понятное» решение: первые три элемента результирующего файла
|
||||
совпадают с контрольными (то есть «правильными»), а прочие элементы отсутствуют:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-8.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<hr>
|
||||
<h3>Верное решение</h3>
|
||||
<p>Приведем, наконец, верное решение задания
|
||||
<code>File48</code>:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
i, a: integer;
|
||||
s: string;
|
||||
f: <b>array </b>[1..4] <b>of file of</b> integer;
|
||||
<b>begin</b>
|
||||
Task('File48');
|
||||
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(s);
|
||||
Assign(f[i], s);
|
||||
<b>if</b> i < 4 <b>then</b> Reset(f[i])
|
||||
<b>else</b> Rewrite(f[i]);
|
||||
<b>end</b>;
|
||||
<b>while</b> <b>not</b> Eof(f[1]) <b>do</b>
|
||||
<b>for</b> i := 1 <b>to</b> 3 <b>do</b>
|
||||
<b>begin</b>
|
||||
read(f[i], a);
|
||||
write(f[4], a);
|
||||
<b>end</b>;
|
||||
<b>for</b> i := 1 <b>to</b> 4 <b>do</b>
|
||||
Close(f[i]);
|
||||
<b>end</b>. </pre></code>
|
||||
</blockquote>
|
||||
|
||||
<p>От предыдущего варианта данное решение отличается добавлением заголовка цикла <code><b>while not</b> Eof(f[1]) <b>do</b></code>,
|
||||
который обеспечивает считывание всех элементов из исходных файлов (напомним, что по условию задания все исходные
|
||||
файлы имеют <i>одинаковый размер</i>) и запись их в результирующий файл в нужном порядке.
|
||||
После запуска этого варианта мы получим сообщение «<i>Верное решение. Тест номер 1 (из 5)</i>»,
|
||||
а после пяти подобных запусков — сообщение «<i>Задание выполнено!</i>»:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/file48-9.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<hr>
|
||||
<h3>Просмотр результатов выполнения задания</h3>
|
||||
<p>Щелкнув мышью на метке «Результаты (F2)», расположенной в правом верхнем
|
||||
углу окна задачника, или нажав клавишу <b>F2</b>, мы можем вывести на экран <i>окно результатов</i>,
|
||||
в котором будет перечислены все наши попытки решения задачи:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
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 Задание выполнено!
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
|
||||
<p>Для закрытия окна результатов достаточно нажать клавишу <b>Esc</b>.
|
||||
Окно результатов можно отобразить на экране и после закрытия окна задачника и возврата в среду PascalABC.NET.
|
||||
Для этого надо использовать команду меню «Модули | Просмотреть результаты», кнопку
|
||||
<img border="0" src="gif/Results.gif" width="16" height="17"> или клавиатурную
|
||||
комбинацию <b>Shift+Ctrl+R</b>.
|
||||
</p>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,535 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
|
||||
<title></title>
|
||||
<link rel="StyleSheet" href="../../default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<H1>Задания на указатели и динамические структуры данных</H1>
|
||||
|
||||
<hr>
|
||||
|
||||
<H2>Пример 1. Анализ существующей динамической структуры</H2>
|
||||
<hr>
|
||||
|
||||
<p>В заданиях группы Dynamic мы встречаемся с двумя новыми видами данных: это <i>динамические структуры</i>,
|
||||
реализованные в виде цепочек связанных друг
|
||||
с другом записей типа <code>TNode</code>, и <i>указатели</i> типа <code>PNode</code> на записи <code>TNode</code>:
|
||||
<code>PNode = ^TNode</code>. Типы <code>TNode</code> и <code>PNode</code> не являются стандартными типами языка Паскаль;
|
||||
они определены в задачнике Programming Taskbook следующим образом (приводятся только те поля записи <code>TNode</code>, которые
|
||||
используются при выполнении заданий группы Dynamic):</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>type</b>
|
||||
PNode = ^TNode;
|
||||
TNode = <b>record</b>
|
||||
Data: integer;
|
||||
Next: PNode;
|
||||
Prev: PNode;
|
||||
. . .
|
||||
<b>end</b>;
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
<p>На примере задания Dynamic2 рассмотрим особенности, связанные с
|
||||
использованием этих новых типов данных.</p>
|
||||
|
||||
<hr>
|
||||
<h3>Создание программы-заготовки и знакомство с заданием</h3>
|
||||
|
||||
<p>Программа-заготовка для задания Dynamic2, созданная с помощью команды
|
||||
меню «Модули | Создать шаблон программы», кнопки
|
||||
<img border="0" src="gif/Load.gif" width="16" height="17"> или клавиатурной
|
||||
комбинации <b>Shift+Ctrl+L</b>, имеет следующий вид:</p>
|
||||
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
|
||||
<b>begin</b>
|
||||
Task('Dynamic2');
|
||||
|
||||
<b>end</b>.
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
|
||||
<p>После запуска данной программы на экране появится <a href="../PT4/window.html">окно задачника</a>:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic2-1.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
<p>Это окно содержит в качестве исходных и результирующих данных новые элементы: динамические структуры и указатели.</p>
|
||||
|
||||
|
||||
<p>Начнем с описания того, как отображается на экране <i>динамическая структура</i>. Для ее вывода используются две
|
||||
экранные строки; в первой строке отображаются имена указателей, связанных с данной структурой,
|
||||
а во второй — содержимое элементов этой структуры, то есть значения их полей <code>Data</code> и способ связи
|
||||
между ними. Вся информация о динамической структуре отображается бирюзовым цветом (подобно информации об элементах файлов).</p>
|
||||
|
||||
|
||||
<p>Рассмотрим в качестве примера динамическую структуру, указанную на рисунке:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
P<sub>1</sub>
|
||||
75 - 65 - 22 - 26 - 10 >nil
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
<p>Этот текст означает, что структура состоит из 5 элементов, причем ее первый элемент имеет поле <code>Data</code>,
|
||||
равное 75, и связан с помощью своего поля <code>Next</code> со вторым элементом, поле <code>Data</code>
|
||||
которого равно 65, и так далее до последнего, пятого элемента, поле <code>Data</code> которого равно 10,
|
||||
а поле <code>Next</code> равно <code>nil</code>, что является признаком завершения структуры.
|
||||
Таким образом, текст, описывающий данную динамическую структуру, является максимально упрощенным вариантом
|
||||
следующей схемы:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic2-2.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Поскольку эта структура указана в разделе исходных данных, следовательно, после инициализации задания
|
||||
она <i>уже существует</i> и размещается в некоторой области динамической памяти (подобно тому, как исходные файлы
|
||||
после инициализации задания размещаются в каталоге учащегося).</p>
|
||||
<p>Как получить доступ к этой существующей динамической структуре? Здесь также уместна аналогия с файлами.
|
||||
Для доступа к внешнему файлу необходимо знать его <i>имя</i>, и в любом задании на обработку файлов
|
||||
имена исходных файлов входят в набор исходных данных. Для доступа к данным,
|
||||
размещенным в динамической памяти, необходимо знать их <i>адрес</i>, и поэтому в любом задании на
|
||||
обработку динамических структур в набор исходных данных входят <i>указатели</i>, содержащие адреса этих структур.</p>
|
||||
<p>Из текста, описывающего динамическую структуру, видно, что на ее первый элемент указывает указатель
|
||||
с именем <code>P<sub>1</sub></code>, который также содержится в наборе исходных данных. Описание этого указателя имеет вид</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
P<sub>1</sub> = ptr
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
<p>Здесь текст <code>P<sub>1</sub> =</code> является <i>комментарием</i> и выделяется, как обычный комментарий,
|
||||
светло-серым цветом, а текст <code>ptr</code> означает, что этот элемент исходных данных является <i>указателем</i>,
|
||||
который надо ввести в программу с помощью процедуры ввода <code>read</code>.</p>
|
||||
<p><b>Замечание.</b> Может возникнуть вопрос: почему вместо условного текста «ptr» не отображается «настоящее» значение указателя
|
||||
(то есть некоторый четырехбайтный адрес)? Это связано с тем, что, даже выведя это значение на экран,
|
||||
мы не сможем определить, с какими данными связан этот адрес, поэтому подобная информация на экране будет излишней.</p>
|
||||
<p>Итак, слово <code>ptr</code> в разделе исходных или результирующих данных означает, что соответствующий элемент данных
|
||||
является <i>указателем</i>, причем непустым (для пустого указателя используется слово <code><b>nil</b></code>).
|
||||
Определить, с каким элементом динамической структуры данных связан непустой указатель, можно по экранной
|
||||
информации об этой динамической структуре.
|
||||
Разумеется, при чтении указателя программа учащегося получит «настоящий» адрес, с помощью которого она сможет
|
||||
обратиться к исходной динамической структуре.</p>
|
||||
<p>Аналогично, создав (или преобразовав) некоторую динамическую
|
||||
структуру, программа учащегося должна передать задачнику некоторый адрес,
|
||||
связанный в этой структурой (используя процедуру вывода <code>write</code>).
|
||||
Зная этот адрес, задачник сможет проверить правильность созданной структуры.</p>
|
||||
|
||||
<hr>
|
||||
<h3>Приступаем к решению</h3>
|
||||
|
||||
<p>Вернемся к заданию
|
||||
Dynamic2. В нем не требуется ни создавать, ни преобразовывать исходную структуру данных;
|
||||
ее необходимо лишь проанализировать,
|
||||
а именно, определить значения всех ее элементов, подсчитать количество элементов и, кроме того,
|
||||
вывести указатель на последний элемент этой структуры.</p>
|
||||
<p>Приведем вначале неполное решение задачи, выводящее все необходимые данные, кроме указателя на последний элемент:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
p1: PNode;
|
||||
n: integer;
|
||||
<b>begin</b>
|
||||
Task('Dynamic2');
|
||||
read(p1);
|
||||
n := 0;
|
||||
<b>while</b> p1 <> nil <b>do</b>
|
||||
<b>begin</b>
|
||||
write(p1^.Data);
|
||||
n := n + 1;
|
||||
p1 := p1^.Next;
|
||||
<b>end</b>;
|
||||
write(n);
|
||||
<b>end</b>.
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
<p>После запуска программы можно убедиться, что все числовые результирующие данные найдены правильно,
|
||||
однако из-за того, что не выведен указатель
|
||||
на последний элемент, решение признано ошибочным с диагностикой «<i>Выведены не все результирующие данные</i>»:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic2-3.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Добавим в конец программы оператор</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
write(p1);
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
<p>После запуска нового варианта программы все требуемые данные будут выведены, однако результирующее значение
|
||||
указателя будет равно <code>nil</code>. Это связано
|
||||
с тем, что после завершения цикла <code><b>while</b></code> в переменной <code>p1</code> содержится <i>нулевой указатель</i>,
|
||||
а не указатель на последний элемент динамической структуры:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic2-4.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<h3>Правильное решение</h3>
|
||||
|
||||
<p>Для того чтобы получить правильное решение, опишем вспомогательную переменную <code>p2</code>,
|
||||
в которой будем сохранять адрес элемента, <i>предшествующего</i> элементу с адресом <code>p1</code>.
|
||||
После завершения цикла <code><b>while</b></code> в этой переменной будет храниться адрес последнего элемента
|
||||
динамической структуры:</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
p1,p2: PNode;
|
||||
n: integer;
|
||||
<b>begin</b>
|
||||
Task('Dynamic2');
|
||||
read(p1);
|
||||
n := 0;
|
||||
<b>while</b> p1 <> nil <b>do</b>
|
||||
<b>begin</b>
|
||||
write(p1^.Data);
|
||||
n := n + 1;
|
||||
p2 := p1; { сохраняем адрес текущего элемента }
|
||||
p1 := p1^.Next; { и переходим к следующему элементу }
|
||||
<b>end</b>;
|
||||
write(n, p2);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
<p>Запустив эту программу три раза, мы получим сообщение «<i>Задание выполнено!</i>»:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic2-5.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<H2>Пример 2. Добавление элемента к динамической структуре</H2>
|
||||
<hr>
|
||||
<h3>Знакомство с заданием</h3>
|
||||
|
||||
|
||||
<p>Рассмотрим простейшее задание, связанное с добавлением элемента к динамической структуре-стеку:
|
||||
Dynamic3.</p>
|
||||
<p>При ознакомительном запуске этого задания мы обнаружим новое обозначение в тексте, описывающем результирующий стек,
|
||||
а именно, точки, обрамляющие первый элемент стека:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic3-1.png">
|
||||
</blockquote>
|
||||
<p>Точки обозначают элементы динамической структуры, память для которых <i>должна быть выделена программой учащегося</i>
|
||||
(в отличие от тех элементов, которые размещаются в памяти самим задачником).</p>
|
||||
<hr>
|
||||
<h3>Приступаем к решению</h3>
|
||||
|
||||
|
||||
<p>Что произойдет, если динамическая структура будет создана с ошибками? Для того чтобы это выяснить, вернем в программе, решающей задание
|
||||
Dynamic3, указатель на прежнюю вершину стека, не добавляя к ней новый элемент:</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
d: integer;
|
||||
p1: PNode;
|
||||
<b>begin</b>
|
||||
Task('Dynamic3');
|
||||
read(d, p1);
|
||||
write(p1);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>После запуска данной программы окно задачника примет вид:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic3-2.png">
|
||||
</blockquote>
|
||||
<p>Скобки вокруг каждого элемента результирующего стека означают, что эти элементы созданы самим задачником, но <i>располагаются
|
||||
не на тех позициях, на которых они должны находиться при правильном решении</i>. Действительно, тот элемент,
|
||||
который в решении является первым, должен (после добавления нового элемента) оказаться вторым и т. д. Итак,
|
||||
наличие скобок в тексте результирующей динамической структуры означает, что ее элементы располагаются не в том порядке,
|
||||
который требуется.</p>
|
||||
<hr>
|
||||
<h3>Правильное решение</h3>
|
||||
|
||||
|
||||
<p>Для получения правильного решения задания Dynamic3 необходимо явно выделить память для нового элемента,
|
||||
используя процедуру <code>New</code>, и заполнить поля этого элемента, связав его с текущей вершиной стека
|
||||
(в результате сам этот элемент станет новой вершиной, адрес которой и следует вывести):</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
d: integer;
|
||||
p1, p2: PNode;
|
||||
<b>begin</b>
|
||||
Task('Dynamic3');
|
||||
read(d, p1);
|
||||
New(p2);
|
||||
p2^.Data := D;
|
||||
p2^.Next := p1;
|
||||
write(p2);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>Приведем вид окна задачника при первом запуске этой программы:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic3-3.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<H2>Пример 3. Удаление элемента из динамической структуры</H2>
|
||||
<hr>
|
||||
<h3>Знакомство с заданием</h3>
|
||||
|
||||
|
||||
<p>Рассмотрим простейшее задание на удаление элемента из динамической структуры —
|
||||
Dynamic5.
|
||||
В нем требуется удалить из стека вершину и вернуть указатель на новую вершину, то есть на элемент,
|
||||
расположенный непосредственно за удаленным.</p>
|
||||
<p>Особенность заданий на удаление элементов из динамических структур заключается в том, что удаляемый элемент
|
||||
необходимо не только «отсоединить»
|
||||
от исходной динамической структуры, но и полностью «уничтожить», то есть <i>освободить память, занимаемую этим элементом</i>.</p>
|
||||
<p>Для того чтобы напомнить учащемуся о необходимости уничтожения некоторых элементов исходной динамической
|
||||
структуры, эти элементы выделяются на экране синим цветом меньшей яркости, чем обычные элементы (на рисунке
|
||||
таким способом выделен элемент 15):</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic5-1.png">
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h3>Приступаем к решению</h3>
|
||||
|
||||
|
||||
<p>Вначале приведем неправильный вариант решения, в котором
|
||||
не освобождается память, занимаемая удаленным из стека элементом:</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b> p1: PNode;
|
||||
<b>begin</b>
|
||||
Task('Dynamic5');
|
||||
read(p1);
|
||||
write(p1^.Data, p1^.Next);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>Несмотря на то что все результирующие данные будут совпадать с контрольными
|
||||
(то есть текст в разделах «Полученные результаты» и «Пример верного решения» будет одинаковым), на
|
||||
информационной панели появится сообщение об ошибке «<i>Не освобождена динамическая память</i>»,
|
||||
а в разделе исходных данных будет выделен красным цветом тот элемент, который требовалось удалить:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic5-2.png">
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h3>Правильное решение</h3>
|
||||
|
||||
|
||||
<p>Для получения правильного решения достаточно добавить в конец программы оператор вызова
|
||||
процедуры <code>Dispose</code>, освобождающий память, на которую указывает указатель <code>p1</code>:</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b> p1: PNode;
|
||||
<b>begin</b>
|
||||
Task('Dynamic5');
|
||||
read(p1);
|
||||
write(p1^.Data, p1^.Next);
|
||||
Dispose(p1);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>Приведем вид окна задачника при первом запуске этой программы:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic5-3.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<H2>Пример 4. Двусвязные динамические структуры</H2>
|
||||
<hr>
|
||||
<h3>Знакомство с заданием</h3>
|
||||
<p>Особенности работы с двусвязными динамическими структурами рассмотрим на примере задания
|
||||
Dynamic30,
|
||||
в котором требуется преобразовать исходную односвязную структуру в двусвязную. Запустив программу-заготовку,
|
||||
созданную для этого задания, мы увидим в области исходных данных информацию об «обычной» односвязной структуре,
|
||||
подобной рассмотренным в предыдущих примерах:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic30-1.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Динамическая структура, приведенная в разделе результатов, имеет две особенности:
|
||||
во-первых, ее элементы связаны символом <code>=</code>, а во-вторых, перед первым элементом присутствует текст
|
||||
<code>nil<</code>.</p>
|
||||
<p>Это означает, что результирующая структура является <i>двусвязной</i>, то есть каждый ее элемент связан не
|
||||
только с последующим элементом (с помощью поля <code>Next</code>, как в односвязной структуре), но и с
|
||||
предыдущим элементом (с помощью нового поля <code>Prev</code>), а поле <code>Prev</code> первого элемента имеет
|
||||
значение <code>nil</code>:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic30-2.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<h3>Приступаем к решению</h3>
|
||||
|
||||
<p>Для преобразования исходной односвязной структуры в двусвязную необходимо задать правильные значения для
|
||||
полей <code>Prev</code> всех элементов структуры, перебирая в цикле пары соседних элементов:</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
p1, p: PNode;
|
||||
<b>begin</b>
|
||||
Task('Dynamic30');
|
||||
read(p1);
|
||||
p := p1^.Next;
|
||||
<b>while</b> p <> nil <b>do</b>
|
||||
<b>begin</b>
|
||||
p^.Prev := p1;
|
||||
p1 := p1^.Next;
|
||||
p := p^.Next;
|
||||
<b>end</b>;
|
||||
write(p1); <i>{ вывод указателя на последний элемент }</i>
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>В этой программе мы определили поля <code>Prev</code> для всех элементов, кроме первого. Поэтому решение будет считаться ошибочным
|
||||
(обратите внимание на то, что перед первым элементом полученного списка отсутствует текст <code>nil<</code>):</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic30-3.png">
|
||||
</blockquote>
|
||||
<p><b>Замечание.</b> При анализе ошибочного решения часто оказывается полезным и специальное обозначение «<code>=</code>» для двойной связи.
|
||||
Предположим, например, что информация
|
||||
о результирующей двусвязной структуре, созданной программой, имеет вид:</p>
|
||||
<blockquote><code><pre>
|
||||
P<sub>2</sub>
|
||||
nil< 33 = 64 - 78 = 12 = 51 >nil
|
||||
</pre></code></blockquote>
|
||||
<p>Это означает, что между вторым и третьим элементом структуры имеется не двойная, а <i>одинарная</i> связь
|
||||
(поле <code>Next</code> второго элемента содержит адрес третьего элемента, а поле <code>Prev</code> третьего элемента
|
||||
<i>не содержит</i> адрес второго).</p>
|
||||
<hr>
|
||||
<h3>Правильное решение</h3>
|
||||
<p>Для получения правильного решения достаточно добавить в программу перед циклом <code>while</code>
|
||||
следующий оператор:</p>
|
||||
<blockquote><code><pre>
|
||||
p1^.Prev := nil;
|
||||
</pre></code></blockquote>
|
||||
<p>Приведем вид окна задачника при первом запуске исправленной программы:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic30-4.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
<p><b>Замечание.</b> Для задания Dynamic30 возможен более короткий вариант решения,
|
||||
в котором не требуется особо обрабатывать первый элемент списка:
|
||||
</p>
|
||||
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
p1, p: PNode;
|
||||
<b>begin</b>
|
||||
Task('Dynamic30');
|
||||
p := nil;
|
||||
read(p1);
|
||||
<b>while</b> p1 <> nil <b>do</b>
|
||||
<b>begin</b>
|
||||
p1^.Prev := p;
|
||||
p := p1;
|
||||
p1 := p1^.Next;
|
||||
<b>end</b>;
|
||||
write(p);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<H2>Пример 5. Циклические динамические структуры</H2>
|
||||
<hr>
|
||||
<h3>Знакомство с заданием</h3>
|
||||
<p>Динамическая структура называется <i>циклической</i>, если она замкнута в «кольцо», то есть ее последний элемент
|
||||
связан полем <code>Next</code> с первым (в случае двусвязной структуры требуется также, чтобы ее первый элемент
|
||||
был связан полем <code>Prev</code>
|
||||
с последним элементом). Простейшим заданием на циклические структуры является
|
||||
Dynamic55, в котором требуется
|
||||
преобразовать обычный двусвязный список
|
||||
в циклический.</p>
|
||||
<p>Запустив программу-заготовку для этого задания, мы увидим
|
||||
на экране изображение двух динамических структур, причем исходная структура является «обычным» двусвязным списком,
|
||||
а результирующая структура — циклическим двусвязным списком:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic55-1.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Обозначения <code><< =</code> и <code>= >></code> позволяют отличить циклический список от обычного
|
||||
(напомним, что у обычного двусвязного списка поле <code>Prev</code> первого элемента и поле <code>Next</code> последнего
|
||||
элемента равны <code><b>nil</b></code>).</p>
|
||||
<p>Таким образом, экранный текст, описывающий циклический двусвязный список, является упрощенным вариантом
|
||||
следующей схемы:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic55-2.png">
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h3>Приступаем к решению</h3>
|
||||
<p>Для решения задания Dynamic55 достаточно найти последний элемент исходного списка и связать его с
|
||||
первым элементом:</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
p1, p2: PNode;
|
||||
<b>begin</b>
|
||||
Task('Dynamic55');
|
||||
read(p1);
|
||||
p2 := p1;
|
||||
<b>while</b> p2^.Next <> <b>nil</b> <b>do</b>
|
||||
p2 := p2^.Next;
|
||||
p2^.Next := p1;
|
||||
write(p2);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>В данном варианте решения мы «забыли» о том, что надо связать не только последний элемент с первым,
|
||||
но и первый с последним (поскольку наш список — двусвязный). Поэтому решение оказалось ошибочным
|
||||
(обратите внимание на то, что после последнего элемента полученного списка изображена одинарная, а не двойная черта):</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic55-3.png">
|
||||
</blockquote>
|
||||
|
||||
<hr>
|
||||
<h3>Правильное решение</h3>
|
||||
<p>Для получения правильного решения достаточно добавить в программу перед процедурой вывода <code>write</code>
|
||||
следующий оператор:</p>
|
||||
<blockquote><code><pre>
|
||||
p1^.Prev := p2;
|
||||
</pre></code></blockquote>
|
||||
<p>Приведем вид окна задачника при первом запуске исправленной программы:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Dynamic55-4.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<h3>Просмотр результатов выполнения заданий</h3>
|
||||
<p>Щелкнув мышью на метке «Результаты (F2)», расположенной в правом верхнем
|
||||
углу окна задачника, или нажав клавишу <b>F2</b>, мы можем вывести на экран <i>окно результатов</i>,
|
||||
в котором будет перечислены все наши попытки решения задачи:</p>
|
||||
|
||||
<blockquote><code><pre>
|
||||
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 Задание выполнено!
|
||||
</pre></code></blockquote>
|
||||
<p>Для закрытия окна результатов достаточно нажать клавишу <b>Esc</b>.
|
||||
Окно результатов можно отобразить на экране и после закрытия окна задачника и возврата в среду PascalABC.NET.
|
||||
Для этого надо использовать команду меню «Модули | Просмотреть результаты», кнопку
|
||||
<img border="0" src="gif/Results.gif" width="16" height="17"> или клавиатурную
|
||||
комбинацию <b>Shift+Ctrl+R</b>.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
|
||||
<title></title>
|
||||
<link rel="StyleSheet" href="../../default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<H1>Задание на обработку строк</H1>
|
||||
|
||||
<hr>
|
||||
<h3>Создание программы-заготовки и знакомство с заданием</h3>
|
||||
|
||||
<p>В качестве примера задания на обработку строк рассмотрим задание String9.</p>
|
||||
<p>Программу-заготовку для решения этого задания можно создать с помощью
|
||||
команды
|
||||
меню «Модули | Создать шаблон программы», кнопки
|
||||
<img border="0" src="gif/Load.gif" width="16" height="17"> или клавиатурной
|
||||
комбинации <b>Shift+Ctrl+L</b>. Эта заготовка будет иметь следующий вид:</p>
|
||||
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
|
||||
<b>begin</b>
|
||||
Task('String9');
|
||||
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
<p>После запуска данной программы на экране появится <a href="../PT4/window.html">окно задачника</a>:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/string9-1.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Данные типа <code>char</code> и <code>string</code> в окне задачника заключаются в <i>апострофы</i>; это позволяет, в частности,
|
||||
отличить числовые данные (например, <code>9</code>) от символьных и строковых данных, содержащих цифры
|
||||
(например, <code>'9'</code>).
|
||||
Кроме того, апострофы дают возможность увидеть <i>пробелы</i>, находящиеся в начале или конце строк.
|
||||
|
||||
<hr>
|
||||
<h3>Ввод исходных данных</h3>
|
||||
<p>Добавим в программу фрагмент, обеспечивающий ввод исходных данных
|
||||
(мы намеренно ввели данные не в том порядке, в котором они указаны в окне задачника):
|
||||
</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
n: integer;
|
||||
c1, c2: char;
|
||||
<b>begin</b>
|
||||
Task('String9');
|
||||
read(c1, c2, n);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
|
||||
|
||||
<p>Запуск нового варианта программы уже не будет считаться ознакомительным, поскольку в программе
|
||||
выполняется ввод исходных данных. Так как порядок ввода исходных данных является ошибочным,
|
||||
этот вариант решения будет признан неверным и приведет к сообщению
|
||||
«<i>Неверно указан тип при вводе исходных данных</i>»:</p>
|
||||
|
||||
<blockquote>
|
||||
<img border="0" src="gif/string9-2.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Общее правило, определяющее порядок ввода и вывода данных для задачника <b>Programming Taskbook</b>
|
||||
гласит:
|
||||
<i>ввод и вывод данных производится по строкам (слева направо), а строки просматриваются сверху вниз</i>.
|
||||
Иными словами,
|
||||
данные, отображаемые в окне задачника, вводятся и выводятся в том порядке, в котором читается обычный
|
||||
текст.</p>
|
||||
<p>На панели индикаторов, расположенной под информационной панелью,
|
||||
отмечено, что введен всего один элемент исходных данных (из трех), хотя в процедуре <code>read</code> нашей программы
|
||||
были
|
||||
указаны три переменные. Это объясняется тем, что при вводе первой из этих переменных (<code>с1</code>)
|
||||
была обнаружена ошибка несоответствия типа, поэтому задачник не стал анализировать остальные данные,
|
||||
которые программа пыталась ввести. Здесь проявляется еще одно правило задачника
|
||||
<b>Programming Taskbook</b>: <i>при обнаружении первой ошибки ввода-вывода
|
||||
анализ оставшихся исходных данных и результатов не проводится</i>.
|
||||
</p>
|
||||
<p>Исправим нашу программу, изменив порядкок параметров в процедуре ввода:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
read(n, c1, c2);
|
||||
</pre></code></blockquote>
|
||||
<p>Теперь с вводом данных проблем не возникает, однако из-за того что в программе отсутствует вывод результирующих
|
||||
данных, решение по-прежнему считается ошибочным:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/string9-3.png">
|
||||
</blockquote>
|
||||
<p>Данная ошибка, в отличие от предыдущей, связана не с вводом исходных данных, а с выводом результатов.
|
||||
Это отмечается в окне задачника двумя способами: цветной маркер ошибки располагается рядом с индикатором вывода,
|
||||
и этим же цветом выделяется заголовок раздела результатов (который в данном случае не содержит никаких данных).
|
||||
</p>
|
||||
<hr>
|
||||
<h3>Формирование и вывод требуемой строки</h3>
|
||||
<p>Для формирования нужной строки воспользуемся операцией <code>+</code> (<i>сцепления</i> строк):</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
n, i: integer;
|
||||
c1, c2: char;
|
||||
s: string;
|
||||
<b>begin</b>
|
||||
Task('String9');
|
||||
read(n, c1, c2);
|
||||
s := '';
|
||||
<b>for</b> i := 1 <b>to</b> n <b>do</b>
|
||||
s := s + c1 + c2;
|
||||
write(s);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>Заметим, что в операторе <code>s := ''</code> нет необходимости, так как все глобальные переменные в Паскале автоматически
|
||||
инициализируются «нулевыми» значениями (для числовых данных это обычные нули, для символов — это
|
||||
символ <code>#0</code>, для
|
||||
строк — пустая строка <code>''</code>).</p>
|
||||
<p>Результат выполнения этой программы будет следующим:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/string9-4.png">
|
||||
</blockquote>
|
||||
<p>Красная звездочка, расположенная в конце выведенной строки, означает, что длина полученной строки превышает
|
||||
длину верной строки. Для того чтобы увидеть на экране всю полученную строку, достаточно подвести курсор мыши
|
||||
к строке со звездочкой; при этом полный текст строки появится во всплывающей подсказке:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/string9-5.png">
|
||||
</blockquote>
|
||||
<p><b>Замечание.</b> Красная звездочка может появиться и при выводе ошибочных числовых данных. Например, если ожидается
|
||||
целое число в диапазоне от <code>1</code> до <code>99</code>, а получено число <code>10000</code>, то на экране изобразится первая цифра этого большого
|
||||
числа, за которой будет указана красная звездочка: <code>1<font color=red>*</font></code>.</p>
|
||||
<hr>
|
||||
<h3>Верное решение</h3>
|
||||
<p>Ошибка в предыдущей программе возникла из-за неверного указания количества итераций цикла. Действительно,
|
||||
на каждой итерации к строке добавляется по <i>два</i> символа, поэтому после <code>n</code> итераций
|
||||
строка будет содержать <code>2*n</code> символов (а не <code>n</code>, как требуется в задании).
|
||||
<p>Для исправления ошибки достаточно вдвое
|
||||
уменьшить число итераций:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
n, i: integer;
|
||||
c1, c2: char;
|
||||
s: string;
|
||||
<b>begin</b>
|
||||
Task('String9');
|
||||
read(n, c1, c2);
|
||||
s := '';
|
||||
<b>for</b> i := 1 <b>to</b> n <b>div</b> 2 <b>do</b>
|
||||
s := s + c1 + c2;
|
||||
write(s);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>После запуска этого варианта мы получим сообщение «<i>Верное решение. Тест номер 1 (из 5)</i>»,
|
||||
а после пяти подобных запусков — сообщение «<i>Задание выполнено!</i>»:</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/string9-6.png">
|
||||
</blockquote>
|
||||
<p><b>Замечание.</b> Приведем другой, более быстрый, способ решения задания String9,
|
||||
в котором результирующая строка заполняется <i>посимвольно</i>, как обычный массив:</p>
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>var</b>
|
||||
n, i: integer;
|
||||
c1, c2: char;
|
||||
s: string;
|
||||
<b>begin</b>
|
||||
Task('String9');
|
||||
read(n, c1, c2);
|
||||
SetLength(s, n);
|
||||
<b>for</b> i := 1 <b>to</b> n <b>div</b> 2 <b>do</b>
|
||||
<b>begin</b>
|
||||
s[2 * i - 1] := c1;
|
||||
s[2 * i] := c2;
|
||||
<b>end</b>;
|
||||
write(s);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>Обратите внимание на процедуру <code>SetLength(s, n)</code>, которая обеспечивает правильную настройку <i>длины</i> результирующей
|
||||
строки <code>s</code>. Без вызова этой процедуры программа работала бы неверно, так как любая
|
||||
глобальная строковая переменная инициализируется пустой строкой, а при работе с отдельными символами строки
|
||||
корректировка ее длины не производится.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<hr>
|
||||
<h3>Просмотр результатов выполнения задания</h3>
|
||||
<p>Щелкнув мышью на метке «Результаты (F2)», расположенной в правом верхнем
|
||||
углу окна задачника, или нажав клавишу <b>F2</b>, мы можем вывести на экран <i>окно результатов</i>,
|
||||
в котором будет перечислены все наши попытки решения задачи:</p>
|
||||
|
||||
<blockquote>
|
||||
<code><pre>
|
||||
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 Задание выполнено!
|
||||
</pre></code>
|
||||
</blockquote>
|
||||
<p>Для закрытия окна результатов достаточно нажать клавишу <b>Esc</b>.
|
||||
Окно результатов можно отобразить на экране и после закрытия окна задачника и возврата в среду <b>PascalABC.NET</b>.
|
||||
Для этого надо использовать команду меню «Модули | Просмотреть результаты», кнопку
|
||||
<img border="0" src="gif/Results.gif" width="16" height="17"> или клавиатурную
|
||||
комбинацию <b>Shift+Ctrl+R</b>.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,314 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
|
||||
<title></title>
|
||||
<link rel="StyleSheet" href="../../default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a name="main"></a>
|
||||
|
||||
<h1>Задания на обработку деревьев
|
||||
</h1>
|
||||
<hr>
|
||||
<h2>Пример 1. Анализ бинарного дерева
|
||||
</h2>
|
||||
<hr>
|
||||
<p>В заданиях группы Tree, как и в заданиях группы Dynamic, мы встречаемся с двумя новыми видами данных:
|
||||
это <i>древовидные динамические структуры</i>, реализованные в виде наборов связанных друг с
|
||||
другом записей типа <code>TNode</code>, и <i>указатели</i> типа <code>PNode</code> на записи <code>TNode</code>: <code>PNode =
|
||||
^TNode</code>. <a href="..\PT4\IO.html#node">Типы <code>TNode</code> и <code>PNode</code></a> не являются стандартными типами языка Паскаль; они
|
||||
определены в задачнике Programming Taskbook.
|
||||
</p>
|
||||
<p>Особенности, связанные с использованием новых типов данных, рассмотрим на
|
||||
примере задания Tree2.
|
||||
</p>
|
||||
<p class="task">Tree2°.
|
||||
Дан адрес <i>P</i><sub>1</sub> записи типа TNode — корня дерева.
|
||||
Эта запись связана полями Left и Right с другими записями того же типа (дочерними вершинами),
|
||||
они, в свою очередь, — со своими дочерними вершинами, и так далее до записей,
|
||||
поля Left и Right которых равны nil (у некоторых вершин может быть равно nil одно из полей Left или Right).
|
||||
Вывести количество вершин дерева.
|
||||
</p>
|
||||
<hr>
|
||||
<h3>Создание программы-заготовки и знакомство с заданием</h3>
|
||||
<p>Напомним, что программу-заготовку для решения этого задания можно создать с помощью
|
||||
команды
|
||||
меню «Модули | Создать шаблон программы», кнопки
|
||||
<img border="0" src="gif/Load.gif" width="16" height="17"> или клавиатурной
|
||||
комбинации <b>Shift+Ctrl+L</b>. Приведем текст созданной заготовки:
|
||||
</p>
|
||||
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
|
||||
<b>begin</b>
|
||||
Task('Tree2');
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>После запуска программы на экране появится <a href="../PT4/window.html">окно задачника</a>:
|
||||
</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Tree2-1.png">
|
||||
</blockquote>
|
||||
|
||||
|
||||
<p>Это окно содержит в качестве исходных и результирующих данных новые
|
||||
элементы: бинарные деревья и указатели.
|
||||
</p>
|
||||
<p>Начнем с описания того, как отображается на экране <i>дерево</i>. Для его вывода используется
|
||||
несколько экранных строк. На каждой строке изображаются вершины дерева,
|
||||
находящиеся на определенном уровне (номер уровня указывается слева от изображения дерева).
|
||||
Для каждой вершины выводится ее значение, т. е. значение поля Data соответствующей записи типа TNode.
|
||||
Любая вершина соединяется линиями со своими дочерними вершинами, расположенными на следующем уровне дерева;
|
||||
левая дочерняя вершина изображается слева от родительской вершины, а правая — справа.
|
||||
Отсутствие у вершины одной или обеих дочерних вершин означает, что ее поля <code>Left</code> и/или <code>Right</code> равны <code>nil</code>.
|
||||
</p>
|
||||
<p>
|
||||
Рассмотрим в качестве примера дерево, приведенное на рисунке.
|
||||
Корень этого дерева имеет значение 15, левая дочерняя вершина корня равна 58,
|
||||
правая дочерняя вершина равна 42, глубина дерева равна 4. Все листья дерева находятся на уровнях
|
||||
3 и 4; листья на уровне 3 имеют значения 15 и 11, листья на уровне 4 — значения 38 и 84.
|
||||
Некоторые из внутренних вершин дерева имеют по две
|
||||
дочерние вершины (это корень и вершины со значениями 55 и 20), некоторые по одной:
|
||||
левой (вершины 42, 87 и 60) или правой (вершина 58).
|
||||
</p>
|
||||
<p>Поскольку это дерево указано в разделе исходных данных, следовательно, после инициализации
|
||||
задания оно уже существует и размещается в некоторой области динамической памяти.
|
||||
Для доступа к данным, размещенным в динамической памяти,
|
||||
необходимо знать их адрес, поэтому в любом задании на обработку деревьев в набор исходных данных входят
|
||||
<i>указатели</i>, содержащие адреса каких-либо вершин этих деревьев (как правило, указывается адрес корня дерева).
|
||||
</p>
|
||||
<p>Работа с исходными и результирующими
|
||||
данными типа указателя подробно обсуждается в разделе, посвященном
|
||||
<a href="ctpointer.html">линейным динамическим структурам</a>.
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<h3>Решение задачи</h3>
|
||||
|
||||
<p>В задании Tree2 не требуется ни создавать, ни
|
||||
преобразовывать исходное дерево;
|
||||
его необходимо лишь проанализировать, а именно определить количество его вершин.
|
||||
</p>
|
||||
<p>Для выполнения этого задания, как и для подавляющего большинства других заданий на обработку деревьев,
|
||||
следует воспользоваться вспомогательной <i>рекурсивной</i> подпрограммой (функцией или процедурой).
|
||||
Рекурсивная природа алгоритмов,
|
||||
связанных с обработкой деревьев (в частности, бинарных деревьев), объясняется тем, что сами
|
||||
определения деревьев общего вида и бинарных деревьев являются рекурсивными.
|
||||
Так, дать словесное описание функции <code>NodeCount(P)</code>, подсчитывающей число вершин дерева с корнем,
|
||||
с которым связан указатель <code>P</code>, можно следующим образом: если указатель <code>P</code> равен <code>nil</code>,
|
||||
то следует вернуть значение <code>0</code>; в противном случае следует вернуть значение
|
||||
<code>1 + NodeCount(P^.Left) + NodeCount(P^.Right)</code>
|
||||
(в этом выражении первое слагаемое соответствует корню дерева, второе —
|
||||
его левому поддереву, а третье — его правому поддереву; при этом не требуется проверять,
|
||||
что указанные поддеревья существуют, так как при их отсутствии соответствующее слагаемое просто будет равно нулю).
|
||||
</p>
|
||||
<p>Таким образом, решение задачи будет иметь следующий вид:
|
||||
</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>function</b> NodeCount(P: PNode): integer;
|
||||
begin
|
||||
<b>if</b> P = nil <b>then</b>
|
||||
result := 0
|
||||
<b>else</b>
|
||||
result := 1 + NodeCount(P^.Left) + NodeCount(P^.Right);
|
||||
<b>end</b>;
|
||||
<b>var</b> P1: PNode;
|
||||
<b>begin</b>
|
||||
Task('Tree2');
|
||||
read(P1);
|
||||
write(NodeCount(P1));
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>Цепочка рекурсивных вызовов функции <code>NodeCount</code> завершается при достижении терминальной вершины (листа),
|
||||
у которой поля <code>Left</code> и <code>Right</code> равны <code>nil</code>. Благодаря наличию функции <code>NodeCount</code>,
|
||||
раздел операторов программы является очень кратким: в нем считывается адрес <code>P1</code> корня исходного дерева,
|
||||
после чего вызывается функция <code>NodeCount(P1)</code>, возвращаемое значение которой сразу выводится процедурой write.
|
||||
</p>
|
||||
<p>Запустив эту программу пять раз, мы получим сообщение «<i>Задание
|
||||
выполнено!</i>».
|
||||
</p>
|
||||
<hr>
|
||||
<a name="parent"></a>
|
||||
<h2>Пример 2. Бинарные деревья с обратной связью
|
||||
</h2>
|
||||
<hr>
|
||||
<p>Рассмотренная выше реализация бинарных деревьев позволяет легко переходить от родительских вершин
|
||||
к их дочерним вершинам, но не допускает обратного перехода. В то же время, для некоторых задач,
|
||||
связанных с обработкой деревьев, возможность обратного перехода от потомков к их предку позволяет
|
||||
получить более простое решение. Ясно, что для обеспечения возможности обратного перехода
|
||||
каждую вершину дерева надо снабдить еще одним полем связи, в котором должна храниться ссылка на
|
||||
ее родительскую вершину. Это поле связи естественно назвать <code>Parent</code>. Поскольку корень дерева
|
||||
предка не имеет, его поле <code>Parent</code> должно быть равно <code>nil</code>.
|
||||
</p>
|
||||
<p>Деревья, вершины которых содержат информацию о своих родителях, будем называть <i>деревьями с обратной связью</i>.
|
||||
Особенности работы с подобными деревьями рассмотрим на примере задания Tree49.
|
||||
</p>
|
||||
<p class="task">Tree49°.
|
||||
Дан указатель <i>P</i><sub>1</sub> на корень дерева, вершинами которого являются записи типа TNode,
|
||||
связанные между собой с помощью полей Left и Right. Используя поле Parent записи TNode,
|
||||
преобразовать исходное дерево в <i>дерево с обратной связью</i>, в котором каждая вершина
|
||||
связана не только со своими дочерними вершинами (полями Left и Right), но и с родительской
|
||||
вершиной (полем Parent). Поле Parent корня дерева положить равным nil.
|
||||
</p>
|
||||
<p>Запустив программу-заготовку, созданную для задания Tree49,
|
||||
мы увидим в области исходных данных изображение «обычного» бинарного дерева,
|
||||
в то время как в области результатов будет изображено дерево с обратной связью,
|
||||
вершины которого связаны не одинарными, а двойными линиями.
|
||||
</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Tree49-1.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Обратите также внимание на то, что в области результатов отсутствуют какие-либо данные, кроме
|
||||
измененного дерева. Это означает, что в программе, решающей задачу, не требуется использовать
|
||||
процедуры вывода; достаточно лишь преобразовать исходное дерево требуемым образом.
|
||||
Поскольку при таком преобразовании адрес корня дерева <i>P</i><sub>1</sub>
|
||||
не изменится, задачник сможеть получить доступ к
|
||||
этому дереву и проверить его правильность.
|
||||
</p>
|
||||
<p>Для преобразования исходного дерева в дерево с обратной связью необходимо задать правильные значения
|
||||
для полей <code>Parent</code> всех вершин дерева, перебирая эти вершины с помощью подходящей рекурсивной процедуры.
|
||||
В эту процедуру удобно передавать в качестве параметров не только указатель <code>P</code> на текущую вершину,
|
||||
но и указатель <code>Par</code> на предка этой вершины:
|
||||
</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>procedure</b> SetParent(P, Par: PNode);
|
||||
<b>begin</b>
|
||||
<b>if</b> P = nil <b>then</b>
|
||||
exit;
|
||||
P^.Parent := Par;
|
||||
SetParent(P^.Left, P);
|
||||
SetParent(P^.Right, P);
|
||||
<b>end</b>;
|
||||
<b>var</b> P1: PNode;
|
||||
<b>begin</b>
|
||||
Task('Tree49');
|
||||
read(P1);
|
||||
SetParent(P1, nil);
|
||||
<b>end</b>.
|
||||
</pre></code></blockquote>
|
||||
<p>При стартовом запуске рекурсивной процедуры <code>SetParent</code> в качестве
|
||||
второго параметра указывается <code>nil</code>.
|
||||
</p>
|
||||
<p>
|
||||
<b>Примечание.</b> Обозначение для двойной связи может оказаться полезным при анализе ошибочного решения.
|
||||
Так, если в изображении дерева с обратной связью имеется вершина, соединенная со своей родительским
|
||||
вершиной не двойной, а одинарной линией, значит, у этой вершины поле <code>Parent</code> содержит ошибочное значение
|
||||
(например, равно <code>nil</code>).
|
||||
</p>
|
||||
<hr>
|
||||
<a name="general"></a>
|
||||
<h2>Пример 3. Деревья общего вида
|
||||
</h2>
|
||||
<hr>
|
||||
<p>С помощью связанных записей типа <code>TNode</code> можно моделировать не только бинарные деревья,
|
||||
но и произвольные упорядоченные деревья, вершины которых имеют любое число непосредственных потомков
|
||||
(будем называть такие деревья <i>деревьями общего вида</i>; для них также используется название
|
||||
«деревья с множественным ветвлением»).
|
||||
Рассмотрим задание Tree86 — первое из заданий, связанных с деревьями общего вида, в котором
|
||||
описываются особенности подобных деревьев.
|
||||
</p>
|
||||
<p class="task">Tree86°.
|
||||
<i>Дерево общего вида</i> (каждая вершина которого может иметь произвольное число дочерних вершин,
|
||||
расположенных в фиксированном порядке в направлении слева направо) реализуется с помощью набора
|
||||
связанных записей типа TNode следующим образом: для каждой внутренней вершины ее поле Left
|
||||
содержит указатель на ее первую (т. е. левую) дочернюю вершину, а поле Right — указатель на ее
|
||||
правую <i>сестру</i>, т. е. вершину, имеющую в дереве общего вида того же родителя.
|
||||
Поле Right корня дерева общего вида всегда равно nil, так как корень сестер не имеет.
|
||||
Дан указатель <i>P</i><sub>1</sub> на корень непустого бинарного дерева. Создать дерево общего вида,
|
||||
соответствующее исходному бинарному дереву, и вывести указатель <i>P</i><sub>2</sub> на его корень.
|
||||
</p>
|
||||
<p>Приведем пример дерева общего вида, которое реализовано с помощью связанных
|
||||
записей типа <code>TNode</code> (аналогичным образом деревья общего вида изображаются в окне задачника):
|
||||
</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Tree86-1.png">
|
||||
</blockquote>
|
||||
|
||||
<p>Корень этого дерева (со значением 13) имеет три дочерние вершины (71, 73 и 29),
|
||||
причем вершина 71 не имеет потомков, вершина 73 имеет три непосредственных потомка (18, 93 и 92),
|
||||
а вершина 29 — два (24 и 84). На последнем уровне располагается вершина 46, являющаяся
|
||||
единственной дочерней вершиной вершины 93.
|
||||
</p>
|
||||
<p>При ознакомительном запуске задания Tree86 на экране появится окно, подобное следующему.
|
||||
</p>
|
||||
<blockquote>
|
||||
<img border="0" src="gif/Tree86-2.png">
|
||||
</blockquote>
|
||||
<p>Обратите внимание на то, как выглядит одно и то же дерево в двух различных представлениях:
|
||||
вариант, соответствующий обычному бинарному дереву, приводится в разделе исходных данных,
|
||||
а вариант, соответствующий дереву общего вида, — в разделе результатов.
|
||||
При переходе от бинарного дерева к дереву общего вида часть информации о структуре бинарного дерева
|
||||
теряется, поскольку в случае, если некоторая вершина дерева общего вида имеет только одного
|
||||
непосредственного потомка, нельзя определить, каким был этот потомок в исходном бинарном дереве — левым или правым.
|
||||
</p>
|
||||
<p>Напомним, что точки, обрамляющие значения вершин в разделе результатов, означают, что
|
||||
все эти вершины должны быть созданы программой учащегося (в отличие от вершин исходного дерева, созданных
|
||||
самим задачником при инициализации задания).
|
||||
</p>
|
||||
<p>При формировании нового дерева будем использовать рекурсивную функцию <code>CreateNode(P)</code>.
|
||||
Параметр <code>P</code> содержит указатель на вершину исходного дерева, копия которой создается при вызове функции.
|
||||
Возвращаемым значением функции является указатель на созданную вершину (как обычно, если <code>P = nil</code>,
|
||||
то функция не выполняет никаких действий и возвращает <code>nil</code>). Для создания дочерних вершин выполняется
|
||||
рекурсивный вызов этой функции. Заметим, что цепочка дочерних вершин может быть пустой
|
||||
(если вершина <code>P</code> является листом), содержать один элемент (если вершина <code>P</code> имеет только одного
|
||||
непосредственного потомка) или два элемента. Перед формированием цепочки дочерних вершин удобно
|
||||
занести адреса дочерних вершин вершины <code>P</code> во вспомогательные переменные <code>P1</code> и <code>P2</code>. При этом в случае,
|
||||
если вершина <code>P</code> имеет только одного потомка (неважно, левого или правого), адрес этого потомка
|
||||
заносится в переменную <code>P1</code>, а переменная <code>P2</code> остается равной <code>nil</code>. Благодаря использованию
|
||||
переменных <code>P1</code> и <code>P2</code>, фрагмент кода, отвечающий за формирование списка дочерних вершин,
|
||||
удается сделать более кратким. Приведем текст программы, решающей задачу Tree86.
|
||||
</p>
|
||||
<blockquote><code><pre>
|
||||
<b>uses</b> PT4;
|
||||
<b>function</b> CreateNode(P: PNode): PNode;
|
||||
<b>var</b> P1, P2: PNode;
|
||||
<b>begin</b>
|
||||
<b>if</b> P = nil <b>then</b>
|
||||
<b>begin</b>
|
||||
result := nil;
|
||||
exit;
|
||||
<b>end</b>;
|
||||
New(result);
|
||||
result^.Data := P^.Data;
|
||||
result^.Right := nil;
|
||||
P1 := P^.Left;
|
||||
P2 := P^.Right;
|
||||
<b>if</b> P1 = nil <b>then</b>
|
||||
<b>begin</b>
|
||||
P1 := P2;
|
||||
P2 := nil;
|
||||
<b>end</b>;
|
||||
<i>{ формирование списка дочерних вершин }</i>
|
||||
result^.Left := CreateNode(P1);
|
||||
<b>if</b> P1 <> nil <b>then</b>
|
||||
result^.Left^.Right := CreateNode(P2);
|
||||
<b>end</b>;
|
||||
<b>var</b> P1: PNode;
|
||||
<b>begin</b>
|
||||
Task('Tree86');
|
||||
read(P1);
|
||||
write(CreateNode(P1));
|
||||
end.
|
||||
</pre></code></blockquote>
|
||||
<p>
|
||||
<b>Примечание.</b> Фрагмент дерева общего вида, содержащий все дочерние вершины некоторой вершины,
|
||||
можно рассматривать как <i>односвязный список</i>, элементы которого связаны между собой
|
||||
с помощью поля <code>Right</code> (у последнего элемента списка поле <code>Right</code> равно <code>nil</code>).
|
||||
Каждый элемент подобного списка может содержать «подсписок» своих дочерних элементов;
|
||||
адрес начала этого подсписка хранится в поле <code>Left</code> данного элемента.
|
||||
Поэтому в алгоритмах, связанных с обработкой вершин деревьев общего вида,
|
||||
для перебора непосредственных потомков некоторой вершины удобно использовать <i>цикл</i>
|
||||
(как при переборе элементов списка), в то время как для обработки каждой дочерней вершины следует,
|
||||
как обычно, использовать <i>рекурсию</i>.
|
||||
</p>
|
||||
|
||||
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
<param name="Keyword" value="Sort">
|
||||
<param name="Keyword" value="SortDescending">
|
||||
<param name="Keyword" value="Swap">
|
||||
<param name="Keyword" value="ToString">
|
||||
<param name="Keyword" value="Transform">
|
||||
<param name="Keyword" value="расширение"></object>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251"><link rel="StyleSheet" href="../../../default.css">
|
||||
|
|
@ -187,6 +188,8 @@
|
|||
<br> Сортирует массив по убыванию по ключу</td></tr>
|
||||
<tr><td><code><b>function</b> Swap<T>(Self: <b>array</b> <b>of</b> T; i, j: integer): <b>array</b> <b>of</b> T;</code>
|
||||
<br> Меняет местами элементы массива с индексами i и j</td></tr>
|
||||
<tr><td><code><b>function</b> ToString(Self: <b>array</b> <b>of</b> real; digits: integer): string;</code>
|
||||
<br> Возвращает строковое представление массива вещественных чисел с фиксированным количеством знаков после десятичной точки</td></tr>
|
||||
<tr><td><code><b>function</b> Transform<T>(Self: <b>array</b> <b>of</b> T; f: T->T): <b>array</b> <b>of</b> T;</code>
|
||||
<br> Преобразует элементы массива по заданному правилу</td></tr>
|
||||
<tr><td><code><b>function</b> Transform<T>(Self: <b>array</b> <b>of</b> T; f: (T,integer)->T): <b>array</b> <b>of</b> T;</code>
|
||||
|
|
|
|||
|
|
@ -191,6 +191,8 @@
|
|||
<br> Возвращает произведение элементов последовательности</td></tr>
|
||||
<tr><td><code><b>function</b> Product(Self: <b>sequence </b> <b>of</b> integer): int64;</code>
|
||||
<br> Возвращает произведение элементов последовательности</td></tr>
|
||||
<tr><td><code><b>function</b> Product(Self: <b>sequence </b> <b>of</b> int64): int64;</code>
|
||||
<br> Возвращает произведение элементов последовательности</td></tr>
|
||||
<tr><td><code><b>function</b> Product<T>(Self: <b>sequence </b> <b>of</b> T; f: T->real): real;</code>
|
||||
<br> Возвращает произведение элементов последовательности, спроектированных на числовое значение</td></tr>
|
||||
<tr><td><code><b>function</b> Product<T>(Self: <b>sequence </b> <b>of</b> T; f: T->integer): int64;</code>
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.11.0.3699
|
||||
3.11.0.3700
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.11.0.3699'
|
||||
!define VERSION '3.11.0.3700'
|
||||
|
|
|
|||
|
|
@ -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<T>(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
|
||||
|
|
|
|||
|
|
@ -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<T>(Self: sequence of T; f: T->real): real; extensionmethod;
|
||||
begin
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in a new issue