Иногда список строк (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();
Результат:
Как обычно, с поддержкой русского язкы все весело, но чинить эту поддержку в данной заметке не буду. Кому сильно надо — пишите.
Алгоритм:
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 > Index2
0
— Если строки равны
-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
Обсуждения на Исходниках, но более подробнее и лучше у меня.