Толик Панков
hex_laden
............ .................. ................

October 2030
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

Back December 2nd, 2022 Forward
На мотив "Подмосковных вечеров"

Чтоб приплыл к тебе,
Сука-Вовочка...
Синекольчатый осьминог.



Это репост с сайта http://tolik-punkoff.com
Оригинал: https://tolik-punkoff.com/2022/12/02/na-motiv-podmoskovnyh-vecherov/

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 > 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

По мотивам


Обсуждения на Исходниках, но более подробнее и лучше у меня.

Это репост с сайта http://tolik-punkoff.com
Оригинал: https://tolik-punkoff.com/2022/12/02/lazarus-sortirovka-tstringlist/

Tags: ,
Back December 2nd, 2022 Forward