Шаблонные списки в EPCL: exList
Введение
В данном документе я постараюсь подробно и понятно описать, как пользоваться компонентом
TGenericList, являющимся частью
EPCL и соспредоточенном в ней в исходном файле
exList.pas
Подключение
Для того, чтобы использовать шаблонный список, необходимо добавить в uses ExList
uses exList
Специализация
Так как TGenericList является шаблоном, перед использованием необходимо специализировать его так:
type TMyList = specialize TGenericList<TMyType>;
ещё примеры:
type
TMyIntegers = specialize TGenericList<integer>; //это специализирует шаблонный
//список на целых числах
TMyReals = specialize TGenericList<real>; //на реальных числах
TMyMultiIntegers = specialze TGenericList<TMyIntegers>; // на списках целых чисел,
//а почему бы и нет?
Создание
Итак, мы объявили список. Перед использованием его надо создать одним из этих способов:
//пример для списка чисел. Для других - аналогично
var
ints: TMyIntegers;
begin
ints:=TMyIntegers.Create; //Способ #1
ints:=TMyIntegers.Create(owner); //тут указываем собственника: TComponent
ints:=TMyIntegers.Create([1, 2, 3, 66, 13, x, -1, -100500, a + b + 1]);
//очень полезный способ, который позволяет создать
//список и сразу запихнуть в него элементы
ints:=TMyIntegers.Create(owner, [1, 2, 3, 6]); //то же, но с собственником
ints:=TMyIntegers.Create(owner, @NewIntFunction, @CompareIntsFunction, @DestroyIntFunction);
//позволяет создать список и присвоить ему функции для создания нового элемента,
//сравнения элементов и удаления элементов. Опционально
ints2:=TMyIntegers.Create(ints.DirectItems); //Копия ints
Добавка элементов
Итак, мы создали список. Теперь, возможно, потребуется добавить в него элементы:
(* Способ #1 *)
ints.Add(15); //добавит элемент 15. Возвращает номер нового элемента
ints.Add(x); //Добавит элемент x Возвращает номер нового элемента
(* Способ #2 *)
ints.Add([15, x, x+15, 6, 7, 8]); //Добавить несколько элементов сразу.
//Возвращает номер последнего добавленного элемента
(* Способ #3 *)
ints.NewFunction:=@CreateNewInt;
ints.AddNew(100); //добавляет 100 новых
При этом CreateNewInt - функция вида
function CreateNewInt: integer;
begin result:=random(100); end;
(* Общий вид: *) function CreateNewItem: TItem;
(* Способ #4 - важный*)
ints.Capacity:=100;
for i:=1 to 100 do ints.add(random(100));
Если в данном примере опустить строку Capacity, то память выделится
(100 / ints._DefaultCapacityReserve) раз, где
_DefaultCapacityReserve = 3 по умолчанию. То есть, около 30 раз. Что негативно отразится на скорости работы.
Обращение к элементам
ints[i]:=1;
x:=ints[6];
// Примочки для первого и последнего элементов
ints.First:=0;
ints.Last:=999;
//Шестой элемент с конца
ints[ints.Count - 6]:=a + b;
//Проверить, существует ли элемент с номером n
var
x: boolean;
begin
x:=ints.ItemExists[n];
end;
Удаление элементов
(* Способ #1 *)
ints.Delete(10); //удалит десятый элемент и сдвинет
// все последующие элементы влево. Возвращает false, если десятого
// элемента в массиве нет
(* Способ #2 *)
ints.Delete([1, 3, 2]);
// Удалит первый, третий и второй элементы в массиве. При этом
// не важно, что после удаления первого элемента третий станет вторым.
// Будут удалены элементы с теми номерами, какие они имели до удаления
// первого элемента из группы.
// Если хотябы один номер повторится два раза или хотябы одного из
// указанных элементов не существует, подет возвращено false
(* Способ #3)
ints.DestroyItem(6); //Применяет удалитель к шестому элементу
// при этом элемент остаётся, количество элементов не изменяется
// Если удалитель не назначен, будет возвращено false, иначе будет
// возвращён результат работы удалителя
Применение удалителя:
ints.DestroyFunction:=@IntDestroyFunction;
ints.Delete(6);
Здесь к шестому элементу перед его удалением будет пременена функция-удалитель. При этом достаточно установить функцию один раз, после чего она будет применяться при удалении любого элемента из массива.
function IntDestroyFunc(var ax: integer): boolean;
begin ax:=0; end;
// Общий вид:
function ItemDestroyFunction(var aItem: TItem): boolean;
(*
Если функция-удалитель возвратит false для элемента (если удалялся один элемент)
или элементов (если они удалялись [,,,] группой, то Delete возвратит также false
*)
Сортировка
Перед сортировкой элементов нужно указать свойство
SortingFunction либо
SortingMethod
ints.SortingFunction:=IntCompareFunc;
ints.Sort;
где IntCompareFunc имеет вид:
// Объявление
TCompareFunction = function(const ax1, ax2: TItem): integer;
TCompareMethod = function(const ax1, ax2: TItem): integer of object;
// Общий вид
function ItemCompareFunc(const a, b: TItem): integer;
type TMyClass = class
function ItemCompareMeth(const a, b: TItem): integer;
end;
// Частный пример
function IntCompareFunc(const ax, ay: integer): integer;
begin result:=ax - ay; end; //Для сортировки по возрастанию из первого
// вычитаем второе
Для сортировки по возрастанию надо возвращать
Если элементы равны
|
0
|
Если первый элемент больше
|
1
|
Если второй элемент больше
|
-1
|
- Важно помнить, что для сортировки достаточно указать либо метод, либо функцию, но не обязательно всё сразу
При этом, если указаны оба свойства, то будет использоваться
метод
Настраиваемая сортировка
Сущность настраиваемой сортировки заключается в том, что кроме сравниваемых переменных в процедуру передаётся набор параметров в виде TVariantList. Как можно догадаться из названия, это список Variant, также могущий включать в себя указатели на наследников TObject.
Подробнее
пример настраиваемой сортировки находится здесь, в исходниках:
EPCL\samples\list_customsort
Функцию настраиваемой сортировки нужно присваивать в свойство
TGenericList.CustomSorting
type
TMy = record
a, b: integer;
end;
TMyList = specialize TGenericList<TMy>;
function CustomSort(const ax1, ax2: TMy; const args: TVariantList): integer;
var
l: TMyList;
ow: TComponent;
begin
if args['by'] = 'a' then result:=ax1.a - ax2.a;
if args['by'] = 'b' then result:=ax1.b - ax2.b;
l:=args['glist']; // ['glist'] будет всегда хранить ссылку на текущий объект
ow:=args['listowner']; // а тут всегда owner списка, как наследника TComponent.
//освобождается args сразу после сортировки. освобождать не надо.
end;
begin
l:=TMyList.Create;
l.CustomSorting:=@CustomSort;
l.Sort(['by', 'a']);
l.Sort(['v1', x1, 'v2', x2, 'имя_аргумента', значение]);
// 'v1':=x1 'v2':=x2;
В данном случае
значение может быть всё что угодно, что может принять Variant.
Поменять местами элементы
ints.Exchange(10, 11); //Десятый поменять с одиннадцатым
Вставка элементов
list.Insert(3, 68); //вставить "68" на позицию 3
list.Insert(i, myitem);
list.Insert(КУДА, ЧТО);
Освобождение
ints.Free
FreeAndNil(ints);
(* Либо удалить все элементы *)
ints.Clear; //Удалить все элементы
ints.DestroyItems //Применить удалитель ко всем элементам, но оставить их
При этом важно, что если указана
DestroyFunction, то она будет применена ко всем элементам списка. Как в результате вызова
Free, так и в результате вызова
Clear. Иначе (если она не указана) они просто будут удалены. Если в списке были экземрляры классов, то будут удалены только ссылки на них, Free не будет применен автоматически, поэтому целесообразно освобождать их в DestroyFunction.
Текстование
Данную фичу обзовём "текстование", т.е. преобразование содержимого списка в текст: string
ints.TextFunction:=@IntToText; //Указываем функцию
ShowMessage(ints.AsText); //Преобразует массив в текст
ShowMessage(ints.AsText[tlNumbers, tlQuotes, tlBRs, tlHead]);
//Собсна, не обязательно указывать все опции, можно что-то одно или несколько
ShowMessage(AsString( ['Массив как текст: ', ints, ' //Троллоло'] ));
//Да, так тоже будет работать, но AsString требует модуль exStrings
Функция текстования должна преобразовывать один элемент в строку
// пример
function IntAstextFunc(const ax: integer): string;
begin result:=AsString(ax); end;
// общий вид
function ItemAsText(const aItem: TItem): string
Формат AsText([]);
При вызове метода AsText можно указать формат преобразования (а можно и не указывать). Вот описание возможных элементов множества
tlNumbers
|
Ставить номера возле элементов
|
tlQuotes
|
Окружать элементы кавычками
|
tlBRs
|
Каждый элемент на новой строке
|
tlHead
|
Заголовок списка, включающий в себя его ClassName, Name(если имеется) и количество элементов
|
tlNames
|
Имена элементов, для TVariantList
|