Table of Contents

Шаблонные списки в EPCL: exList
Введение
Подключение
Специализация
Создание
Добавка элементов
Обращение к элементам
Удаление элементов
Сортировка
Настраиваемая сортировка
Поменять местами элементы
Вставка элементов
Освобождение
Текстование

Шаблонные списки в 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