Динамические связанные списки
ВВЕДЕНИЕ
В рамках предмета «Учебная практика» мы познакомились с концепцией динамических структур данных и алгоритмами работы с ними, а также рассмотрели возможные варианты их практического использования. Наибольшее внимание было уделено такой динамической структуре, как связанный список и различным его дополнениям и модификациям.
В рамках данного проекта сделана попытка решения практической задачи с использованием связанных списков. При проектировании программного средства были учтены особенности работы медицинских учреждений в реальных условиях, в связи с чем введен ряд правил и естественных ограничений. Использование динамических структур данных позволило оптимизировать расход оперативной памяти и организовать гибкое управление данными.
В данной пояснительной записке приведено описание программного средства и процесса его разработки, включающее следующие пункты:
- постановка задачи
- описание возможностей приложения
- обоснование выбора структур данных на основе сравнительного анализа возможных вариантов
- схематическое изображение алгоритма
- результаты тестирования программы
- руководство пользователя с подробными пояснениями и иллюстрациями
- заключение
- листинг программы с комментариями
1. Постановка задачи
Вариант 6
В магазине имеется список поступивших в продажу CD/DVD дисков. Каждая запись списка содержит: тип хранимой информации (фильм, музыка, СОФТ и т.п.), наименование, автора, цену и примечание (поле м.б. пустым). Требуется:
- Отсортировать внутри каждого типа информацию по наименованию либо по автору (на выбор пользователя);
- Осуществлять поиск диска по автору, по наименованию;
- Предусмотреть возможность добавлять, удалять и корректировать записи из списка, а так же просматривать весь список.
2. Выбор и обоснование структур данных
Возможны несколько вариантов представления и хранения необходимых для работы программы данных.
Рассмотрим некоторые из них
2.1 Статические массивы.
Массив –это набор однотипных компонентов (элементов), расположенных в памяти непосредственно друг за другом, доступ к которым осуществляется по индексу (индексам).
Преимуществом массива по сравнению с динамическими структурами данных является возможность произвольного доступа, т.е. отсутствует необходимость просмотра всех элементов, расположенных в массиве перед тем элементом, к которому требуется обратиться.
При использовании статических массивов существенным недостатком является необходимость выделения памяти под максимальное количество элементов, т.к. нет возможности изменять размер статического массива при добавлении в него нового элемента.
Нерационально использовать статические массивы и при необходимости удаления элементов. В этом случае придется производить большое количество дополнительных операций над оставшимися элементами, а также часть занятой памяти останется неиспользованной.
Т.к. элементами массива в случае поставленной задачи являются переменные типа запись, содержащие несколько полей, то расходы памяти при использовании массивов были бы неоправданно большими.
К тому же, в условиях поставленной задачи не удастся использовать преимущество прямого доступа к элементам, т.к. необходима возможность выбора элемента по различным признакам, в то время как тип индексации одномерного массива определяется единственным образом.
Таким образом, статические массивы не являются оптимальным решением для представления и хранения данных, необходимых для работы программы.
Динамические связанные списки.
Связанный список – это базовая динамическая структура данных, состоящая из узлов, каждый из которых содержит как собственно данные, так и одну или две ссылки («связки») на следующий и/или предыдущий узел списка.
Преимуществом связанных списков по сравнению со статическими массивами является отсутствие ограничений на количество динамически добавляемых элементов (в реальности ограничено только объемом доступной памяти).
Также значительно проще, чем в статических массивах, выполняется процедура удаления.
Недостатком связанных списков является отсутствие возможности произвольного доступа к узлам, т.е. необходимость просмотра всех узлов, предшествующих заданному. Поэтому, в общем случае, работа со списками медленнее, чем с массивами.
К тому же, для хранения указателей на соседние элементы расходуется дополнительное количество памяти.
Базы данных.
База данных – это информационная модель, позволяющая упорядоченно хранить данные о группе объектов, обладающих одинаковым набором свойств.
Преимуществом использования баз данных является удобство и относительная простота работы с данными, т.к. компоненты среды Delphi предоставляют для этого достаточно полные и гибкие возможности.
Множество операций над данными, таких как поиск и фильтрация, с помощью них описываются и выполняются быстрее.
Недостатком баз данных является более низкая скорость работы, т.к. физически база данных представляет собой файл или совокупность файлов, хранимых на внешнем носителе.
Поэтому использование базы данных требует большегообъемапамяти, в т.ч. для хранения служебной информации.
В условиях поставленной задачи, нет необходимости в использовании базы данных, т.к. она эффективна лишь при достаточно большом объеме хранимой информации. В противном случае, использование базы данных приведет к существенному снижению производительности программы и неоправданным затратам памяти.
2.4 Вывод.
Таким образом, проанализировав вышеописанные структуры данных, можно сделать вывод, что в случае поставленной задачи оптимальным решением является использование динамических связанных списков.
В программе используются динамические списки. С целью экономии памяти каждая запись списка содержит указатель только на следующий элемент списка, т.к. используемые алгоритмы позволяют решить задачу с помощью односвязных списков.
Приведенная ниже схема иллюстрирует структуру хранения данных на базе односвязного динамического списка.
Head | PDisk | PDisk | . . . | PDisk |
Data |
Name |
Author |
Price |
Note |
Data |
Name |
Author |
Price |
Note |
Data |
Name |
Author |
Price |
Note |
Рисунок 2.1 – Схема структуры данных
Предложенная структура данных позволяет динамически добавлять CD/DVD диски в «базу данных», роль которой выполняет связанный список элементов типа PDisk. В программе предусмотрено их редактирование и сохранение изменений.
3. Разработка алгоритма
Согласно поставленной задаче алгоритм предусматривает основные возможности работы с данными CD/DVD дисков магазина.
Алгоритм предусматривает следующие возможности:
- добавление в базу данных о CD/DVD дисках (тип, наименование, автор, цена, примечание);
- изменение в базе данных о CD/DVD дисках;
- удаление из базы данных о CD/DVD диске;
- поиск CD/DVD дисков по наименованию либо по автору;
- сортировка списка CD/DVD дисков по наименованию либо по автору;
- автоматическая сортировка списка CD/DVD дисков по их типу при добавлении либо изменение дисков;
- автоматическое сохранение списка CD/DVD дисков в текстовый файл при закрытии программы;
- автоматическая загрузка списка CD/DVD дисков из текстового файла при открытии программы.
Использованные в программе алгоритмы основаны на основных приемах работы с динамическими списками, рассмотренных в соответствующем разделе.
Рисунок 3.1 – Схема программы
Рисунок 3.2 – Схема процедуры добавления диска
Рисунок 3.3 – Схема процедуры изменения диска
Рисунок 3.4 – Схема процедуры удаления диска
Рисунок 3.5 – Схема процедуры поиска диска
Рисунок 3.6 – Схема процедуры сортировки дисков
Рисунок 3.7 – Схема процедуры обновления списка дисков
4. Технические приемы программирования
1) При разработке интерфейса программы были использованы следующие компоненты Delphi: TLabel, TEdit, TCombobox, TButton, TBitBtn, TRadioGroup, TStringGrid, TPanel.
2) В программе используется один динамический список, используемый для хранения данных.
3) Для хранения указателя на начало списка типа PDisk используется глобальная переменная, т.к. доступ к списку должен быть обеспечен в любой точке программы.
4 В программе предусмотрен ряд естественных ограничений (добавление существующего диска, изменение диска на существующий, использование пробелов в поле ''Наименование'' и ''Автор''), реализованных с помощью предварительной проверки состояния списка при клике на соответствующих элементах управления.
5) С целью экономии памяти определена процедура для физического удаления списков.
Пример:
procedure TFrmMain.BtnDeleteClick(Sender: TObject);
var
H, Temp: PDisk;
begin
H := Head;
if Sg.Row <> 0 then
begin
if (H^.Data = Sg.Cells[0, Sg.Row]) and
(H^.Name = Sg.Cells[1, Sg.Row]) and
(H^.Author = Sg.Cells[2, Sg.Row]) and
(inttostr(H^.Price) = Sg.Cells[3, Sg.Row]) and
(H^.Note = Sg.Cells[4, Sg.Row]) then
begin
Temp := Head;
Head := Head^.Next;
Dispose(Temp);
end
else
begin
while (H^.Next^.Data <> Sg.Cells[0, Sg.Row]) or
(H^.Next^.Name <> Sg.Cells[1, Sg.Row]) or
(H^.Next^.Author <> Sg.Cells[2, Sg.Row]) or
(inttostr(H^.Next^.Price) <> Sg.Cells[3, Sg.Row]) or
(H^.Next^.Note <> Sg.Cells[4, Sg.Row]) do
H := H^.Next;
Temp := H^.Next;
H^.Next := H^.Next^.Next;
Dispose(Temp);
end;
GridDeleteRow(Sg.Row);
end;
end;
5. Тестирование
Таблица 5.1 - Результаты тестов подпрограмм
Название подпрограммы | Входные данные | Ожидаемые результаты | Пример вызова | Полученные результаты |
AddNewDisk | Принимает введенные пользователем в поля ввода данные о диске. | Чтение данных, проверка и занесение в список данных о новом диске. | Содержимое полей ввода: фильмы; Начало; Кристофер_Нолан; 25000; интереснейший фильм | Данные из полей ввода прочитаны, проверены на корректность, в список занесен новый диск, содержащий введенные данные. |
Содержимое полей ввода: софт; Total commander; Cristian_ Ghistler; 50000; - | Данные из полей ввода прочитаны, проверены на корректность, но в список новый диск не занесен, т.к. поле наименования диска не должно содержать пробелов, выдано соответствующее сообщение. | |||
ChangeDisk | Принимает введенные пользователем в поля ввода данные о диске. | Чтение данных, проверка и изменение в списке данных о диске. | Содержимое полей ввода: музыка; Жизнь-река; Стас_Михайлов; 30000; эксклюзивное издание | Данные из полей ввода прочитаны, проверены на корректность, в списке изменились данные диска. |
Содержимое полей ввода: фильмы; Начало; Кристофер_Нолан; 25000; интереснейший фильм | Данные из полей ввода прочитаны, проверены на корректность, в списке данные диска не изменились, т.к. диск с такими данными уже имеется, выдано соответствующее сообщение. | |||
DeleteDisk | Принимает данные о диске из полей выделенной строки таблицы. | Поиск и удаление из списка диска с заданными данными. | Содержимое полей строки таблицы: фильмы; Начало; Кристофер_Нолан; 25000; интереснейший фильм | Был найден и удален из списка диск с заданными данными. |
SearchByName | Принимает введенное пользователем в поле ввода наименование диска. | Чтение введенных данных, вывод на экран найденных по данному запросу дисков. | Содержимое поля ввода: жизнь | Данные прочитаны, поиск диска с наименованием, содержащим введенное, на экран был выведен диск . |
Содержимое поля ввода: белазар | Данные прочитаны, поиск диска с наименованием, содержащим введенное, на экран не было выведен диск, т.к. диска с заданным наименованием нет в списке, выдано соответствующее сообщение. | |||
SortByName | Сортирует список дисков по наименованию в алфавитном порядке. | Список успешно отсортировался. |
6. Руководство пользователя
При запуске программы появляется главное окно(рис 6.1).
Рисунок 6.1
На главном окне можно видеть таблицу, а также операции, выполняемые над её элементами. Если после предыдущего запуска программы были сохранены диски, то они автоматически загружаются в таблицу(рис 6.2).
Рисунок 6.2
В программе доступны следующие операции над дисками:
1. Добавить
2. Изменить
3. Удалить
4. Поиск
5. Сортировка
Добавить диск
Для добавления диска в список нажмите на кнопку "Добавить" на панели операций, заполните обязательные поля в открывшейся форме(рис 6.3) и нажмите "Добавить".
Рисунок 6.3
При неверном вводе данных диска могут появиться следующие сообщения об ошибках:
1) При добавлении диска, который уже есть в списке; при изменении диска на такой, который уже есть в списке(рис 6.4):
Рисунок 6.4
2) При добавлении диска, содержащего пробел в поле "наименование" или "автор"(рис 6.5):
Рисунок 6.5
3) При добавлении или изменении диска с незаполненным полем(полями):
Рисунок 6.6
Изменить диск
Для изменения диска выделите строку таблицы, содержащую данные интересующего вас диска, нажмите на кнопку "Изменить" на панели операций, исправьте интересующие вас поля в открывшейся форме(рис 6.7) и нажмите "Изменить".
Рисунок 6.7
При неправильном вводе данных могут возникнуть сообщения об ошибках, описанные в предыдущем подпункте.
Удалить диск
Выделите строку в таблице с данными ненужного вам диска и нажмите на кнопку "Удалить" на панели операций(рис 6.8).
Рисунок 6.8
Поиск диска
Введите наименование или автора диска в соответствующее поле поиска на панели поиска(рис 6.9) и нажмите "ok" для отображения интересующего вас диска(дисков). Для просмотра всего списка дисков и очистки полей поиска нажмите на кнопку "Обновить"(рис 6.10).
Рисунок 6.9
Рисунок 6.10
При поиске несуществующего в списке диска появляется сообщение(рис 6.11):
Рисунок 6.11
Сортировка дисков
Для сортировки списка по наименованию или по автору выберите соответствующий пункт на панели сортировки(6.12).
Рисунок 6.12
По закрытию программы, данные списка дисков автоматически записываются в файл. При следующем запуске программы они будут загружены из файла.
7.Заключение
Как было показано выше, данное приложение предоставляет пользователю возможности, перечисленные при постановке задачи.
При этом для решения задачи использованы структуры данных (динамические связанные списки), выбор которых обоснован в пункте «Выбор и обоснование структур данных». Разработанный алгоритм, созданный при помощи среды программирования Delphi, эффективно использует предоставляемые данной средой возможности. Таким образом, в процессе разработки данного программного средства мы на практике убедились в преимуществах работы с динамическими структурами данных.
В результате тестирования приложения было показано, что полученные результаты работы подпрограмм на конкретных рассмотренных (корректных и некорректных) наборах данных соответствуют ожидаемым, т. е. основные подпрограммы функционируют нормально.
В программе предусмотрен ряд естественных ограничений, продиктованных смыслом поставленной задачи и особенностями ее реализации в действительности. Данные ограничения подробно документированы в пункте «Руководство пользователя».
Ниже приведен листинг программы с подробными комментариями.
Таким образом, данное приложение соответствует задаче, указанной в пункте 1 «Постановка задачи».
8.Листингпрограммы
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Grids, Vcl.Buttons, unit2;
type
PDisk = ^Disk;
Disk = Record
Data: string;
Name: string;
Author: string;
Price: integer;
Note: string;
Next: PDisk;
End;
TFrmMain = class(TForm)
Sg: TStringGrid;
PnlActions: TPanel;
BtnAdd: TButton;
BtnChange: TButton;
BtnDelete: TButton;
EdtAuthorSearch: TEdit;
BtnNameSearch: TButton;
EdtNameSearch: TEdit;
BtnAuthorSearch: TButton;
Rg: TRadioGroup;
LblSearch: TLabel;
LblOperations: TLabel;
PnlOperations: TPanel;
PnlSearch: TPanel;
BtnRefresh: TBitBtn;
procedure FormCreate(Sender: TObject);
procedure BtnAddClick(Sender: TObject);
procedure BtnChangeClick(Sender: TObject);
procedure BtnDeleteClick(Sender: TObject);
procedure BtnNameSearchClick(Sender: TObject);
procedure BtnRefreshClick(Sender: TObject);
procedure BtnAuthorSearchClick(Sender: TObject);
procedure RgClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure SgDrawCell(Sender: TObject; ACol, ARow: integer; Rect: TRect;
State: TGridDrawState);
private
{ Private declarations }
public
procedure Sort(SortType: integer);
{ Public declarations }
end;
var
FrmMain: TFrmMain;
Head: PDisk;
Correct: Boolean = true;
IsAdded: Boolean;
implementation
{$R *.dfm}
// процедура создания сортированного списка
procedure TFrmMain.Sort(SortType: integer);
var
H, N, NewHead, Temp: PDisk;
DiskType, NewDiskType, RowNum, ColNum: integer;
CurName, NewName: String;
begin
H := Head;
NewHead := nil;
// выполнять, пока не пройдёмся по всему текущему списку
while H <> nil do
begin
// DiskType - числовое значение типа текущего диска
if H^.Data = 'фильмы' then
DiskType := 0
else if H^.Data = 'музыка' then
DiskType := 1
else
DiskType := 2;
// если сортированный список не существует, то создаём
if NewHead = nil then
begin
New(NewHead);
NewHead^ := H^;
NewHead^.Next := nil;
end
else
// если сортированный список существует
begin
N := NewHead;
// NewDiskType - числовое значение типа диска из сортированного списка
if N^.Data = 'фильмы' then
NewDiskType := 0
else if N^.Data = 'музыка' then
NewDiskType := 1
else
NewDiskType := 2;
{ если сортированный список состоит только из 1го элемента и у диска из
сортированного списка приоритет выше чем у текущего, то добавляем
текущий после сортированного }
if (DiskType > NewDiskType) and (NewHead^.Next = nil) then
begin
New(N^.Next);
N^.Next^ := H^;
N^.Next^.Next := nil;
end
else
// если предыдущее условие не выполнено
begin
// если DiskType = NewDiskType, т.е. приоритеты дисков равны и список состоит из 1го диска
if NewHead^.Next = nil then
begin
{ SortType = 0, если сортируем по наименованию, 1 - если по автору.
CurName и NewName - строковые величины, которые будут сравниваются,
и в зависимости от типа сортировки могут обозначать
наименование(0) или автора(1) диска, CurName - текущий диск,
NewName - диск из сортированного списка }
if SortType = 0 then
begin
CurName := H^.Name;
NewName := N^.Name;
end
else
begin
CurName := H^.Author;
NewName := N^.Author;
end;
// добавляем в сортированный список элемент(диск) из текущего
if AnsiLowerCase(CurName) > AnsiLowerCase(NewName) then
begin
New(NewHead^.Next);
NewHead^.Next^ := H^;
NewHead^.Next^.Next := nil;
end
else
begin
Temp := NewHead;
New(NewHead);
NewHead^ := H^;
NewHead^.Next := Temp;
end;
end
// если в сортированном списке элементов больше, чем 1
else
begin
// обновляем приоритет диска из нового списка и сравниваемые величины
if N^.Data = 'фильмы' then
NewDiskType := 0
else if N^.Data = 'музыка' then
NewDiskType := 1
else
NewDiskType := 2;
if SortType = 0 then
begin
CurName := H^.Name;
NewName := N^.Name;
end
else
begin
CurName := H^.Author;
NewName := N^.Author;
end;
// проверяем 1ый элемент сортированного списка
if (DiskType = NewDiskType) and
(AnsiLowerCase(CurName) < AnsiLowerCase(NewName)) then
begin
Temp := NewHead;
New(NewHead);
NewHead^ := H^;
NewHead^.Next := Temp;
end
else
// проверяем остальные элементы
begin
repeat
// если следующего за отсортированным диском не существует, то добавляем после сортированного текущий
if N^.Next = nil then
begin
New(N^.Next);
N^.Next^ := H^;
N^.Next^.Next := nil;
break;
end;
// обновляем сравниваемые величины
if SortType = 0 then
begin
CurName := H^.Name;
NewName := N^.Next^.Name;
end
else
begin
CurName := H^.Author;
NewName := N^.Next^.Author;
end;
{ если (приоритеты текущего диска и следующего сортированного диска
равны и не выполняется условие строкового(алфавитного) сравнения) или
(приоритет текущего диска больше приоритета следующего за сортированным
диска),то переходим на следующий сортированный диск и обновляем приоритет }
if ((DiskType = NewDiskType) and
(AnsiLowerCase(CurName) > AnsiLowerCase(NewName))) or
(DiskType > NewDiskType) then
begin
N := N^.Next;
if N^.Next <> nil then
begin
if N^.Next^.Data = 'фильмы' then
NewDiskType := 0
else if N^.Next^.Data = 'музыка' then
NewDiskType := 1
else
NewDiskType := 2;
end;
end
else
{ иначе, если (приоритеты текущего диска и следующего сортированного диска не
равны или выполняется условие строкового(алфавитного) сравнения) или
(приоритет текущего диска равен приоритету следующего за сортированным
диска),то добавляем текущий диск к сортированным }
begin
Temp := N^.Next;
New(N^.Next);
N^.Next^ := H^;
N^.Next^.Next := Temp;
break;
end;
// повторяем, пока не пройдемся по всему сортированному списку
until N = nil;
end;
end;
end;
end;
// переходим на следующий элемент(диск) текущего списка
H := H^.Next;
{ если прошлись уже по всему текущему списку, значит отсортированный список
готов, неотсортированный список больше не нужен, удаляем его
для экономии памяти(повышение эффективности программы) }
end;
if Head <> nil then
begin
while Head <> nil do
begin
Temp := Head;
Head := Head^.Next;
Dispose(Temp);
end;
Head := NewHead;
// печать отсортированного списка
if IsAdded = true then
Sg.RowCount := Sg.RowCount + 1;
H := Head;
with FrmMain do
for RowNum := 1 to Sg.RowCount - 1 do
begin
for ColNum := 0 to 4 do
begin
case ColNum of
0:
Sg.Cells[ColNum, RowNum] := H^.Data;
1:
Sg.Cells[ColNum, RowNum] := H^.Name;
2:
Sg.Cells[ColNum, RowNum] := H^.Author;
3:
Sg.Cells[ColNum, RowNum] := inttostr(H^.Price);
4:
Sg.Cells[ColNum, RowNum] := H^.Note;
end;
end;
H := H^.Next;
end;
end;
end;
// при нажатии на кнопку 'Добавить'
procedure TFrmMain.BtnAddClick(Sender: TObject);
begin
with FrmNewDisk do
begin
// очищаем поля компонентов на второй форме
CmbDiskType.ItemIndex := 0;
EdtName.Text := '';
EdtAuthor.Text := '';
EdtPrice.Text := '';
EdtNote.Text := '';
BtnAddNewDisk.Visible := true;
BtnChangeDisk.Visible := false;
IsAdded := true;
// открываем вторую форму
FrmNewDisk.ShowModal;
end;
end;
// процедура печати результата поиска
procedure PrintSearch(Searching: string; T: integer);
var
H, Temp, PrintDisk: PDisk;
Comparing: string;
begin
with FrmNewDisk do
with FrmMain do
begin
// PrintDisk - список дисков, которые удовлетворяют условию поиска
PrintDisk := nil;
H := Head;
// проходимся по всему списку
while H <> nil do
begin
{ параметр T передаём при вызове процедуры, если он = 1,
то ищем диски по наименованию, иначе по автору.
Comparing - наименование/автор текущего диска,
Searching - наименование/автор, которого мы хотим найти }
if T = 1 then
Comparing := H^.Name
else
Comparing := H^.Author;
// если строка Searching является строкой/подстрокой строки Comparing
if Pos(AnsiLowerCase(Searching), AnsiLowerCase(Comparing)) <> 0 then
begin
// если PrintDisk пустой, то создаём его и добавляем 1ый элемент(диск)
if PrintDisk = nil then
begin
New(PrintDisk);
PrintDisk^ := H^;
PrintDisk^.Next := nil;
end
else
// если не пустой, то добавляем элемент
begin
Temp := PrintDisk;
while Temp^.Next <> nil do
Temp := Temp^.Next;
New(Temp^.Next);
Temp^.Next^ := H^;
Temp^.Next^.Next := nil;
end;
end;
// проверяем следующий элемент(диск) списка
H := H^.Next;
end;
{ после того, как проверили весь список, если PrintDisk пустой,
показать соответствующее сообщение }
if PrintDisk = nil then
ShowMessage('По данному запросу дисков не найдено.')
else
begin
{ если PrintDisk не пустой, то выводим все его элементы на экран,
последовательно удаляя элементы PrintDisk'a для экономии памяти
(повышение эффективности программы) }
Sg.RowCount := 1;
while PrintDisk <> nil do
begin
Sg.RowCount := Sg.RowCount + 1;
Sg.Cells[0, Sg.RowCount - 1] := PrintDisk^.Data;
Sg.Cells[1, Sg.RowCount - 1] := PrintDisk^.Name;
Sg.Cells[2, Sg.RowCount - 1] := PrintDisk^.Author;
Sg.Cells[3, Sg.RowCount - 1] := inttostr(PrintDisk^.Price);
Sg.Cells[4, Sg.RowCount - 1] := PrintDisk^.Note;
Temp := PrintDisk;
PrintDisk := PrintDisk^.Next;
Dispose(Temp);
end;
end;
end;
end;
// процедура поиска и вывода на экран дисков, поиск производится по автору
procedure TFrmMain.BtnAuthorSearchClick(Sender: TObject);
begin
PrintSearch(EdtAuthorSearch.Text, 2);
end;
// при нажатии на кнопку 'Изменить'
procedure TFrmMain.BtnChangeClick(Sender: TObject);
var
H, Temp: PDisk;
RowNum, ColNum: integer;
begin
with FrmNewDisk do
begin
// если выделили не заглавную строку таблицы
if Sg.Row <> 0 then
begin
{ переносим значения из ячеек строки выделенной таблицы в поля компонентов
на другой форме для дальнейшей работы с изменяемым элементом списка(диском) }
if Sg.Cells[0, Sg.Row] = 'фильмы' then
CmbDiskType.ItemIndex := 0;
if Sg.Cells[0, Sg.Row] = 'музыка' then
CmbDiskType.ItemIndex := 1;
if Sg.Cells[0, Sg.Row] = 'софт' then
CmbDiskType.ItemIndex := 2;
EdtName.Text := Sg.Cells[1, Sg.Row];
EdtAuthor.Text := Sg.Cells[2, Sg.Row];
EdtPrice.Text := Sg.Cells[3, Sg.Row];
EdtNote.Text := Sg.Cells[4, Sg.Row];
BtnAddNewDisk.Visible := false;
BtnChangeDisk.Visible := true;
Correct := false;
// открываем вторую форму для корректировки диска
FrmNewDisk.ShowModal;
// если изменения не были произведены, тогда выходим из процедуры
if Correct = false then
begin
Exit;
end
else
// иначе
begin
H := Head;
// если корректируем 1ую строку (1ый диск списка)}
if Sg.Row = 1 then
begin
// если диск стал иметь тип 'фильмы'
if CmbDiskType.Text = 'фильмы' then
begin
H^.Data := CmbDiskType.Text;
H^.Name := EdtName.Text;
H^.Author := EdtAuthor.Text;
H^.Price := strtoint(EdtPrice.Text);
H^.Note := EdtNote.Text;
{ если ни одна из сортировок не активирована,
то меняем значенияполей в 1ой строке таблицы }
if Rg.ItemIndex = -1 then
begin
Sg.Cells[0, 1] := CmbDiskType.Text;
Sg.Cells[1, 1] := EdtName.Text;
Sg.Cells[2, 1] := EdtAuthor.Text;
Sg.Cells[3, 1] := EdtPrice.Text;
Sg.Cells[4, 1] := EdtNote.Text;
ShowMessage('Диск успешно отредактирован.');
Exit;
end;
end;
// если диск стал иметь тип 'музыка'
if CmbDiskType.Text = 'музыка' then
begin
// запоминаем в Temp меняющуюся строку, текущий диск извлекается из списка
Temp := H;
H := H^.Next;
Head := Head^.Next;
Temp^.Data := CmbDiskType.Text;
Temp^.Name := EdtName.Text;
Temp^.Author := EdtAuthor.Text;
Temp^.Price := strtoint(EdtPrice.Text);
Temp^.Note := EdtNote.Text;
{ если тип первого элемента(диска) списка 'музыка',
то Temp добавляется в список как 1ый диск }
if H^.Data = 'музыка' then
begin
Temp^.Next := H;
Head := Temp;
end
else
// иначе
begin
// если следующий диск в списке существует
if H^.Next <> nil then
// пока тип следующего диска не 'музыка' или не 'софт'
while (H^.Next^.Data <> 'музыка') and
(H^.Next^.Data <> 'софт') do
begin
{ если следующий диск не является последним в списке,
то переходим к следующему }
if H^.Next^.Next <> nil then
H := H^.Next
else
// иначе переходим к следующему и выходим из цикла
begin
H := H^.Next;
break;
end;
end;
// добавляем диск Temp как следующий за текущим
Temp^.Next := H^.Next;
H^.Next := Temp;
end;
end;
// если диск стал иметь тип 'софт'
if CmbDiskType.Text = 'софт' then
begin
// запоминаем в Temp меняющуюся строку, текущий диск извлекается из списка
Temp := H;
H := H^.Next;
Head := Head^.Next;
Temp^.Data := CmbDiskType.Text;
Temp^.Name := EdtName.Text;
Temp^.Author := EdtAuthor.Text;
Temp^.Price := strtoint(EdtPrice.Text);
Temp^.Note := EdtNote.Text;
{ если тип первого элемента(диска) списка 'софт',
то Temp добавляется в список как 1ый диск }
if Head^.Data = 'софт' then
begin
Temp^.Next := Head;
Head := Temp;
end
else
// иначе
begin
// если следующий диск в списке существует
if H^.Next <> nil then
// пока тип следующего диска не 'софт'
while H^.Next^.Data <> 'софт' do
begin
{ если следующий диск не является последним в списке,
то переходим к следующему }
if H^.Next^.Next <> nil then
H := H^.Next
else
// иначе переходим к следующему и выходим из цикла
begin
H := H^.Next;
break;
end;
end;
// добавляем диск Temp как следующий за текущим
Temp^.Next := H^.Next;
H^.Next := Temp;
end;
end;
end
else
// если корректируем не 1ую строку (не 1ый диск списка)}
begin
// запоминаем в Temp меняющуюся строку, текущий диск извлекается из списка
while (H^.Next^.Data <> Sg.Cells[0, Sg.Row]) or
(H^.Next^.Name <> Sg.Cells[1, Sg.Row]) or
(H^.Next^.Author <> Sg.Cells[2, Sg.Row]) or
(inttostr(H^.Next^.Price) <> Sg.Cells[3, Sg.Row]) or
(H^.Next^.Note <> Sg.Cells[4, Sg.Row]) do
H := H^.Next;
Temp := H^.Next;
H^.Next := H^.Next^.Next;
Temp^.Data := CmbDiskType.Text;
Temp^.Name := EdtName.Text;
Temp^.Author := EdtAuthor.Text;
Temp^.Price := strtoint(EdtPrice.Text);
Temp^.Note := EdtNote.Text;
// вставляем её на вершину своего типа
// если диск стал иметь тип 'фильмы'
if CmbDiskType.Text = 'фильмы' then
begin
Temp^.Next := Head;
Head := Temp;
end;
// если диск стал иметь тип 'музыка'
if CmbDiskType.Text = 'музыка' then
begin
H := Head;
{ если тип первого элемента(диска) списка 'музыка',
то Temp добавляется в список как 1ый диск }
if Head^.Data = 'музыка' then
begin
Temp^.Next := Head;
Head := Temp;
end
else
// иначе
begin
// если следующий диск в списке существует
if H^.Next <> nil then
// пока тип следующего диска не 'музыка' или не 'софт'
while (H^.Next^.Data <> 'музыка') and
(H^.Next^.Data <> 'софт') do
begin
{ если следующий диск не является последним в списке,
то переходим к следующему }
if H^.Next^.Next <> nil then
H := H^.Next
else
// иначе переходим к следующему и выходим из цикла
begin
H := H^.Next;
break;
end;
end;
// добавляем диск Temp как следующий за текущим
Temp^.Next := H^.Next;
H^.Next := Temp;
end;
end;
// если диск стал иметь тип 'софт'
if CmbDiskType.Text = 'софт' then
begin
H := Head;
{ если тип первого элемента(диска) списка 'софт',
то Temp добавляется в список как 1ый диск }
if Head^.Data = 'софт' then
begin
Temp^.Next := Head;
Head := Temp;
end
else
// иначе
begin
// если следующий диск в списке существует
if H^.Next <> nil then
// пока тип следующего диска не 'софт'
while H^.Next^.Data <> 'софт' do
begin
{ если следующий диск не является последним в списке,
то переходим к следующему }
if H^.Next^.Next <> nil then
H := H^.Next
else
// иначе переходим к следующему и выходим из цикла
begin
H := H^.Next;
break;
end;
end;
// добавляем диск Temp как следующий за текущим
Temp^.Next := H^.Next;
H^.Next := Temp;
end;
end;
end;
{ если активирована сортировка по наименованию, сортируем
соответствующим образом и выводим на экран список }
if Rg.ItemIndex = 0 then
begin
IsAdded := false;
Sort(0);
ShowMessage('Диск успешно отредактирован.');
Exit;
end;
{ если активирована сортировка по автору, сортируем
соответствующим образом и выводим на экран список }
if Rg.ItemIndex = 1 then
begin
IsAdded := false;
Sort(1);
ShowMessage('Диск успешно отредактирован.');
Exit;
end;
// если никакая сортировка не активирована, то выводим на экран список
H := Head;
for RowNum := 1 to Sg.RowCount - 1 do
begin
for ColNum := 0 to 4 do
begin
case ColNum of
0:
Sg.Cells[ColNum, RowNum] := H^.Data;
1:
Sg.Cells[ColNum, RowNum] := H^.Name;
2:
Sg.Cells[ColNum, RowNum] := H^.Author;
3:
Sg.Cells[ColNum, RowNum] := inttostr(H^.Price);
4:
Sg.Cells[ColNum, RowNum] := H^.Note;
end;
end;
H := H^.Next;
end;
ShowMessage('Диск успешно отредактирован.');
end;
end;
end;
end;
// удаление строки из таблицы
procedure GridDeleteRow(RowNumber: integer);
var
i: integer;
begin
with FrmMain do
begin
Sg.Row := RowNumber;
// если удаляется последняя строка таблицы
if Sg.Row = Sg.RowCount - 1 then
Sg.RowCount := Sg.RowCount - 1
else
begin
// не последняя
for i := RowNumber to Sg.RowCount - 1 do
Sg.Rows[i] := Sg.Rows[i + 1];
Sg.RowCount := Sg.RowCount - 1;
end;
end;
end;
// при нажатии на кнопку 'Удалить'
procedure TFrmMain.BtnDeleteClick(Sender: TObject);
var
H, Temp: PDisk;
begin
H := Head;
// если выделена не заглавная строка таблицы
if Sg.Row <> 0 then
begin
{ если данные первого в списке диска совпадают с данными диска
выделенной строки таблицы, то удаляем первый элемент(диск) из списка,
а также очищаем занимаемую им память }
if (H^.Data = Sg.Cells[0, Sg.Row]) and (H^.Name = Sg.Cells[1, Sg.Row]) and
(H^.Author = Sg.Cells[2, Sg.Row]) and
(inttostr(H^.Price) = Sg.Cells[3, Sg.Row]) and
(H^.Note = Sg.Cells[4, Sg.Row]) then
begin
Temp := Head;
Head := Head^.Next;
Dispose(Temp);
end
else
{ в противном случае ищем совпадения данных текущего диска с данными диска
в выделенной строке таблицы, после чего удаляем этот элемент(диск) из списка
и очищаем занимаемую им память }
begin
while (H^.Next^.Data <> Sg.Cells[0, Sg.Row]) or
(H^.Next^.Name <> Sg.Cells[1, Sg.Row]) or
(H^.Next^.Author <> Sg.Cells[2, Sg.Row]) or
(inttostr(H^.Next^.Price) <> Sg.Cells[3, Sg.Row]) or
(H^.Next^.Note <> Sg.Cells[4, Sg.Row]) do
H := H^.Next;
Temp := H^.Next;
H^.Next := H^.Next^.Next;
Dispose(Temp);
end;
// после физического удаления элемента(диска) из списка удаляем данные о нём из таблицы
GridDeleteRow(Sg.Row);
end;
end;
// процедура поиска и вывода на экран дисков, поиск производится по наименованию
procedure TFrmMain.BtnNameSearchClick(Sender: TObject);
begin
PrintSearch(EdtNameSearch.Text, 1);
end;
{ при нажатии на кнопку 'Обновить' выводит на экран весь список,
а также очищает поля поиска }
procedure TFrmMain.BtnRefreshClick(Sender: TObject);
var
H: PDisk;
begin
H := Head;
Sg.RowCount := 1;
while H <> nil do
begin
Sg.RowCount := Sg.RowCount + 1;
Sg.Cells[0, Sg.RowCount - 1] := H^.Data;
Sg.Cells[1, Sg.RowCount - 1] := H^.Name;
Sg.Cells[2, Sg.RowCount - 1] := H^.Author;
Sg.Cells[3, Sg.RowCount - 1] := inttostr(H^.Price);
Sg.Cells[4, Sg.RowCount - 1] := H^.Note;
H := H^.Next;
end;
EdtNameSearch.Text := '';
EdtAuthorSearch.Text := '';
end;
// при закрытие формы
procedure TFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
var
F: TextFile;
begin
AssignFile(F, 'Data.txt');
if Head <> nil then
begin
// создаём новый файл
Rewrite(F);
// проходимся по каждому диску списка
while Head <> nil do
begin
// записываем информацию о диске в файл
if Head^.Data = 'фильмы' then
write(F, '0 ')
else if Head^.Data = 'музыка' then
write(F, '1 ')
else
write(F, '2 ');
write(F, Head^.Name, ' ');
write(F, Head^.Author, ' ');
write(F, inttostr(Head^.Price), ' ');
{ если текущий диск является последним, то просто дописываем в файл последнее поле диска('примечание') }
if Head^.Next = nil then
write(F, Head^.Note)
else
{ если текущий диск не является последним в списке, то после записи последнего
поля диска('примечание') в файл переходим на следующую строку файла }
writeln(F, Head^.Note);
// переходим к следующему диску
Head := Head^.Next;
end;
// закрываем файл
CloseFile(F);
end
else if FileExists('Data.txt') then
Erase(F);
end;
// при создании формы
procedure TFrmMain.FormCreate(Sender: TObject);
var
F: TextFile;
C: char;
S: string;
H: PDisk;
i: integer;
begin
{ присваиваем заглавным ячейкам таблицы имена,
а также устанавливаем ширину некоторых её столбцов }
Sg.ColWidths[1] := 250;
Sg.ColWidths[2] := 150;
Sg.ColWidths[4] := 495;
Sg.Cells[0, 0] := ' Тип';
Sg.Cells[1, 0] := ' Наименование';
Sg.Cells[2, 0] := ' Автор';
Sg.Cells[3, 0] := ' Цена';
Sg.Cells[4, 0] :=
' Примечание';
// если существует файл с данными
if FileExists('Data.txt') then
begin
// открываем файл F для чтения
AssignFile(F, 'Data.txt');
Reset(F);
// i - номер печатающейся строки
i := 1;
// пока не конец файла
while not Eof(F) do
begin
// если список пуст
if Head = nil then
begin
// создаём список, считываем данные о первом диске из файла
New(Head);
Read(F, C);
case strtoint(C) of
0:
Head^.Data := 'фильмы';
1:
Head^.Data := 'музыка';
2:
Head^.Data := 'софт';
end;
Read(F, C, C);
S := '';
while C <> ' ' do
begin
S := S + C;
Read(F, C);
end;
Head^.Name := S;
S := '';
Read(F, C);
while C <> ' ' do
begin
S := S + C;
Read(F, C);
end;
Head^.Author := S;
S := '';
Read(F, C);
while C <> ' ' do
begin
S := S + C;
Read(F, C);
end;
Head^.Price := strtoint(S);
S := '';
Read(F, C);
while not((Ord(C) = 13) or (Ord(C) = 26)) do
begin
S := S + C;
Read(F, C);
end;
Head^.Note := S;
Head^.Next := nil;
// вывод списка(первого элемента) на экран
Sg.RowCount := Sg.RowCount + 1;
Sg.Cells[0, 1] := Head^.Data;
Sg.Cells[1, 1] := Head^.Name;
Sg.Cells[2, 1] := Head^.Author;
Sg.Cells[3, 1] := inttostr(Head^.Price);
Sg.Cells[4, 1] := Head^.Note;
end
else
// если список не пуст
begin
// добавляем диск в список, считывая данные о нём из файла
Readln(F);
H := Head;
while H^.Next <> nil do
H := H^.Next;
New(H^.Next);
H := H^.Next;
Read(F, C);
case strtoint(C) of
0:
H^.Data := 'фильмы';
1:
H^.Data := 'музыка';
2:
H^.Data := 'софт';
end;
Read(F, C, C);
S := '';
while C <> ' ' do
begin
S := S + C;
Read(F, C);
end;
H^.Name := S;
S := '';
Read(F, C);
while C <> ' ' do
begin
S := S + C;
Read(F, C);
end;
H^.Author := S;
S := '';
Read(F, C);
while C <> ' ' do
begin
S := S + C;
Read(F, C);
end;
H^.Price := strtoint(S);
S := '';
Read(F, C);
while not((Ord(C) = 13) or (Ord(C) = 26)) do
begin
S := S + C;
Read(F, C);
end;
H^.Note := S;
H^.Next := nil;
// вывод на экран очередного диска
Sg.RowCount := Sg.RowCount + 1;
i := i + 1;
Sg.Cells[0, i] := H^.Data;
Sg.Cells[1, i] := H^.Name;
Sg.Cells[2, i] := H^.Author;
Sg.Cells[3, i] := inttostr(H^.Price);
Sg.Cells[4, i] := H^.Note;
end;
end;
// закрываем файл
CloseFile(F);
end;
end;
// при нажатии на вид сортировки
procedure TFrmMain.RgClick(Sender: TObject);
begin
IsAdded := false;
if Rg.ItemIndex = 0 then
// сортируем по наименованию
Sort(0);
if Rg.ItemIndex = 1 then
// сортируем по автору
Sort(1);
end;
end.
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids;
type
TFrmNewDisk = class(TForm)
EdtName: TEdit;
CmbDiskType: TComboBox;
EdtAuthor: TEdit;
EdtPrice: TEdit;
EdtNote: TEdit;
BtnAddNewDisk: TButton;
BtnChangeDisk: TButton;
LblData: TLabel;
LblName: TLabel;
LblAuthor: TLabel;
LblPrice: TLabel;
LblNote: TLabel;
procedure BtnAddNewDiskClick(Sender: TObject);
procedure BtnChangeDiskClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmNewDisk: TFrmNewDisk;
implementation
{$R *.dfm}
uses unit1;
{ функция проверки списка на наличие диска с полями, заполненными так же,
как поля компонентов формы }
function CheckDisk(H: PDisk): boolean;
var
s: string;
begin
Result := false;
with FrmNewDisk do
// проходимся по всему списку
while H <> nil do
begin
case CmbDiskType.ItemIndex of
0:
s := 'фильмы';
1:
s := 'музыка';
2:
s := 'софт';
end;
{ если в списке имеется диск с полями, заполненными так же,
как поля компонентов формы, тогда функции присваивается
соответственный результат, а также выходим из функции }
if (H^.Data = s) and (H^.Name = EdtName.Text) and
(H^.Author = EdtAuthor.Text) and (inttostr(H^.Price) = EdtPrice.Text)
and (H^.Note = EdtNote.Text) then
begin
Result := true;
Exit;
end
else
// иначе проверяем следующий элемент
H := H^.Next;
end;
end;
// процедура добавления диска в список
procedure AddDisk(H: PDisk);
var
i, j: integer;
Temp: PDisk;
begin
{ если в списке имеется диск с полями, заполненными так же,
как поля компонентов формы, выводим соответствующее сообщение }
if CheckDisk(Head) = true then
begin
ShowMessage('Данный диск уже имеется.');
Exit;
end
else
// иначе
with FrmNewDisk do
// если список не существует
if H = nil then
begin
// создаём список
New(Head);
with Head^ do
begin
case CmbDiskType.ItemIndex of
0:
Data := 'фильмы';
1:
Data := 'музыка';
2:
Data := 'софт';
end;
Name := EdtName.Text;
Author := EdtAuthor.Text;
Price := strtoint(EdtPrice.Text);
Note := EdtNote.Text;
Next := nil;
with FrmMain do
begin
// вывод на экран списка, состоящего из 1 элемента(диска)
Sg.RowCount := Sg.RowCount + 1;
Sg.Cells[0, Sg.RowCount - 1] := Head^.Data;
Sg.Cells[1, Sg.RowCount - 1] := Head^.Name;
Sg.Cells[2, Sg.RowCount - 1] := Head^.Author;
Sg.Cells[3, Sg.RowCount - 1] := inttostr(Head^.Price);
Sg.Cells[4, Sg.RowCount - 1] := Head^.Note;
ShowMessage('CD/DVD диск добавлен.');
end;
end;
end
else
begin
// если список существует и добавляем фильм, то добавляем элемент(диск) в список
if CmbDiskType.ItemIndex = 0 then
begin
New(H);
H^.Data := 'фильмы';
H^.Name := EdtName.Text;
H^.Author := EdtAuthor.Text;
H^.Price := strtoint(EdtPrice.Text);
H^.Note := EdtNote.Text;
H^.Next := Head;
Head := H;
with FrmMain do
begin
{ если активирована сортировка по наименованию, сортируем
соответствующим образом и выводим на экран список }
if Rg.ItemIndex = 0 then
begin
Sort(0);
ShowMessage('CD/DVD диск добавлен.');
Exit;
end;
{ если активирована сортировка по автору, сортируем
соответствующим образом и выводим на экран список }
if Rg.ItemIndex = 1 then
begin
Sort(1);
ShowMessage('CD/DVD диск добавлен.');
Exit;
end;
{ если никакая сортировка не активирована,
вставляем в таблицу добавленный элемент(диск) }
Sg.RowCount := Sg.RowCount + 1;
For i := Sg.RowCount - 2 downto 1 do
Sg.Rows[i + 1] := Sg.Rows[i];
Sg.Cells[0, 1] := Head^.Data;
Sg.Cells[1, 1] := Head^.Name;
Sg.Cells[2, 1] := Head^.Author;
Sg.Cells[3, 1] := inttostr(Head.Price);
Sg.Cells[4, 1] := Head^.Note;
ShowMessage('CD/DVD диск добавлен.');
Exit;
end;
end;
// если список существует и добавляем музыку
if CmbDiskType.ItemIndex = 1 then
begin
// j - счётчик строки, в которую будем добавлять новый элемент(диск) списка
j := 1;
// если первый элемент(диск) в списке относится к типу фильмов
if Head^.Data = 'фильмы' then
begin
// пока следующий элемент(диск) списка существует
while H^.Next <> nil do
begin
{ если у следующего диска его тип относится к фильмам,
то переходим к следующему диску, увеличивая счётчик на 1 }
if H^.Next^.Data = 'фильмы' then
begin
H := H^.Next;
j := j + 1;
end
else
{ если у следующего диска его тип не относится к фильмам,
то увеличиваем счётчик на 1 и выходим из цикла }
begin
j := j + 1;
break;
end;
end;
// если дошли до последнего элемента(диска) списка, то увеличиваем счётчик на 1
if H^.Next = nil then
j := j + 1;
// добавляем элемент(диск) в список
Temp := H^.Next;
New(H^.Next);
with H^.Next^ do
begin
Data := 'музыка';
Name := EdtName.Text;
Author := EdtAuthor.Text;
Price := strtoint(EdtPrice.Text);
Note := EdtNote.Text;
Next := Temp;
end;
end
else
// если первый элемент(диск) в списке не относится к типу фильмов
begin
// добавляем в список новый элемент(диск) как первый элемент списка
New(H);
H^.Data := 'музыка';
H^.Name := EdtName.Text;
H^.Author := EdtAuthor.Text;
H^.Price := strtoint(EdtPrice.Text);
H^.Note := EdtNote.Text;
H^.Next := Head;
Head := H;
end;
with FrmMain do
begin
{ если активирована сортировка по наименованию, сортируем
соответствующим образом и выводим на экран список }
if Rg.ItemIndex = 0 then
begin
Sort(0);
ShowMessage('CD/DVD диск добавлен.');
Exit;
end;
{ если активирована сортировка по автору, сортируем
соответствующим образом и выводим на экран список }
if Rg.ItemIndex = 1 then
begin
Sort(1);
ShowMessage('CD/DVD диск добавлен.');
Exit;
end;
{ если никакая сортировка не активирована,
вставляем в таблицу добавленный элемент(диск) }
Sg.RowCount := Sg.RowCount + 1;
For i := Sg.RowCount - 2 downto j do
Sg.Rows[i + 1] := Sg.Rows[i];
Sg.Cells[0, j] := 'музыка';
Sg.Cells[1, j] := EdtName.Text;
Sg.Cells[2, j] := EdtAuthor.Text;
Sg.Cells[3, j] := EdtPrice.Text;
Sg.Cells[4, j] := EdtNote.Text;
ShowMessage('CD/DVD диск добавлен.');
Exit;
end;
end;
// если список существует и добавляем софт
if CmbDiskType.ItemIndex = 2 then
begin
// j - счётчик строки, в которую будем добавлять новый элемент(диск) списка
j := 1;
// если первый элемент(диск) в списке не относится к типу софт
if Head^.Data <> 'софт' then
begin
// пока следующий элемент(диск) списка существует
while H^.Next <> nil do
begin
{ если у следующего диска его тип не относится к софту,
то переходим к следующему диску, увеличивая счётчик на 1 }
if H^.Next^.Data <> 'софт' then
begin
H := H^.Next;
j := j + 1;
end
else
{ если у следующего диска его тип относится к софту,
то увеличиваем счётчик на 1 и выходим из цикла }
begin
j := j + 1;
break;
end;
end;
// если дошли до последнего элемента(диска) списка, то увеличиваем счётчик на 1
if H^.Next = nil then
j := j + 1;
// добавляем элемент(диск) в список
Temp := H^.Next;
New(H^.Next);
with H^.Next^ do
begin
Data := 'софт';
Name := EdtName.Text;
Author := EdtAuthor.Text;
Price := strtoint(EdtPrice.Text);
Note := EdtNote.Text;
Next := Temp;
end;
end
else
// если первый элемент(диск) в списке относится к типу софт
begin
New(H);
H^.Data := 'софт';
H^.Name := EdtName.Text;
H^.Author := EdtAuthor.Text;
H^.Price := strtoint(EdtPrice.Text);
H^.Note := EdtNote.Text;
H^.Next := Head;
Head := H;
end;
with FrmMain do
begin
{ если активирована сортировка по наименованию, сортируем
соответствующим образом и выводим на экран список }
if Rg.ItemIndex = 0 then
begin
Sort(0);
ShowMessage('CD/DVD диск добавлен.');
Exit;
end;
{ если активирована сортировка по автору, сортируем