Lazarus, сортировка TStringList.
ПреамбулаИногда список строк (
TStringList) требуется сортировать, что, как бы, понятно. Но во Freepascal сортировка устроена довольно странно, в C# у аналогичного класса из коробки несколько больше возможностей, во всяком случае, можно поменять направление, по возрастанию или по убыванию. С хитрыми сортировками, конечно, тоже вылезает нетривиальщина, но на то они и хитрые сортировки.
Тестовые спискиДля начала сформируем тестовые списки, для русского и английского языков. Там код тривиален, потому не буду загружать заметку, ссылки на PasteBin:
Тестовый список для английского языкаТестовый список для русского языкаЗаодно уж и выведем списки в их изначальном виде:
Стандартная сортировкаОна же единственная из коробки, является стандартной сортировкой по возрастанию, т.е. список сортируется от меньшего к большему, а об алгоритме будет ниже. Вызов функции простой:
lstTest.Sort;Пробуем. Выводим список на экран:
WriteLn('Сортировка по возрастанию (по-умолчанию, логическая):');
i:=0;
while i < lstTest.Count do begin
WriteLn(lstTest[i]);
inc(i);
end;
Writeln(); WriteLn('Press Enter...'); ReadLn();Результат:

Как обычно, с поддержкой русского язкы все весело, но чинить эту поддержку в данной заметке не буду. Кому сильно надо - пишите.
Стандартный алгоритм сравнения строк во freepascalАлгоритм:
1. Строки сравниваются посимвольно. Например, в словах
parrot и
puppy первые символы (
p и
p) равны, а далее
a меньше
u, т.е
u в кодовой таблице находится ниже, чем
a.
2. На этом сравнение прекращается, строка
puppy больше чем
parrot.
3. Если начальные символы строк совпадают, то в дело вступает длина, чем строка длиннее, тем она больше, поэтому, зависимая от центра Каталония, все еще больше, чем кот, который гуляет сам по себе:
program test;
var
S1:string;
S2:string;
begin
S1:='cat';
S2:='catalonia';
if S1 < S2 then Writeln ('S1 (',S1,') < S2 (',S2,')' );
if S1 = S2 then Writeln ('S1 (',S1,') = S2 (',S2,')' );
if S1 > S2 then Writeln ('S1 (',S1,') > S2 (',S2,')' );
Readln();
end.Вывод:
S1 (cat) < S2 (catalonia)Что, конечно, печально, потому что кот свободнее Каталонии, и явно больше в этом смысле.
Пользовательские функции сортировкиОграничение, накладываемое одной функцией сортировки в комплекте класса
TStringList легко решается тем, что можно подключить любую пользовательскую сортировку ссылкой на внешнюю функцию.
Для этого надо установить пользовательскую функцию сортировки, таким вот образом:
lstTest.CustomSort(@MySort);где
MySort - имя вашей функции сортировки. Естественно, она должна быть заранее создана, чтобы все откомпилировалось и заработало.
Не забудьте про символ @ перед именем функции, в Delphi не надо было его указывать, компилятор сам знал, где вместо имени функции вставить ссылку на ее адрес, в freepascal это надо указывать явно.Формат функции следующий:
MySort(List: TStringList; Index1, Index2: Integer): Integer;Т.е. на входе нужна переменная типа
TStringList и две переменные типа
Integer, для индексов строк в списке, функция должна возвращать значение типа
Integer:
1 - Если строка
Index1 > Index20 - Если строки равны
-1 - Если строка
Index1 < Index2Конечно, по мнению пользовательского алгоритма сортировки. Например, можем повторить стандартный метод сортировки:
function SortByAsc(List: TStringList; Index1, Index2: Integer): Integer;
begin
if List[Index1]>List[Index2] then
begin
Result := 1;
Exit;
end;
if List[Index1]=List[Index2]
then Result := 0
else Result := -1;
end;
Внезапно, починился русский язкы :)
Сортировка по убываниюfunction SortByDesc(List: TStringList; Index1, Index2: Integer): Integer;
begin
if List[Index1]<List[Index2] then
begin
Result := 1;
Exit;
end;
if List[Index1]=List[Index2]
then Result := 0
else Result := -1;
end;Вообще просто, достаточно заменить знак
> на
< в операторе сравнения.
Другие пользовательские сортировкиВозможности пользовательских сортировок не ограничены практически ничем. Покажу, как обычно, самое простое - сортировка строк по длине.
Сортировка по возрастанию:
function SortByLenAsc(List: TStringList; Index1, Index2: Integer): Integer;
begin
if Length(List[Index1])>Length(List[Index2]) then
begin
Result := 1;
Exit;
end;
if Length(List[Index1])=Length(List[Index2])
then Result := 0
else Result := -1;
end;Т.е. просто применяем функцию
Length в операторе сравнения:
Length(List[Index1])
Тот же вариант, но по убыванию:
function SortByLenDesc(List: TStringList; Index1, Index2: Integer): Integer;
begin
if Length(List[Index1])<Length(List[Index2]) then
begin
Result := 1;
Exit;
end;
if Length(List[Index1])=Length(List[Index2])
then Result := 0
else Result := -1;
end;
Тестовые программыПример для английского языка на GitHubПример для русского языка на GitHubПо мотивамОбсуждения на Исходниках, но более подробнее и лучше у меня.
Это репост с сайта http://tolik-punkoff.com
Оригинал: https://tolik-punkoff.com/2022/12/02/lazarus-sortirovka-tstringlist/