GetHashCode для слияния имен

Отличное решение! И самое обидное, что очень эффективное – у нас классический подход через записи 15 секунд работал. Впрочем, это можно будет студентам показать, Pattern Matching работает тут очень хорошо.

Скажите, а почему вот такой вариант не работает? В словаре вроде компаратор используется, но почему-то не хочет сливать разные падежи слов.

type StringsEqualityComparer = class (IEqualityComparer<string>)

    class count : integer;
    public function Equals(s1,s2 : string) : boolean;
    begin
      var w := min(s1.Length, s2.Length)-2;
      count += 1;
      Result := copy(s1,1,w)=copy(s2,1,w);
    end;

    public function GetHashCode(s : string):integer;
    begin
      Result := s.GetHashCode;
    end;
end;

begin
  var w := new System.Net.WebClient();
  var ss := w.DownloadString('http://vojnaimir.ru/files/book1.txt');

  var q := ss.MatchValues('\b\p{Lu}\w{3,}\b');
  var d := new Dictionary<string,integer>(new StringsEqualityComparer());
  foreach var x in q do
    d[x] := d.Get(x) + 1;
  d.OrderByDescending(x->x.Value).Take(10).Print(NewLine);
  
  writeln;
  writeln('Компаратор выполнил сравнений : ',StringsEqualityComparer.count);
  var cmp := new StringsEqualityComparer();
  var s1 := 'Пьер';
  var s2 := 'Пьерa';
  writeln(s1,' = ',s2,' -> ',cmp.equals(s1,s2));
  s2 := 'Андрей';
  writeln(s1,' = ',s2,' -> ',cmp.equals(s1,s2));
end.

Потому что там неправильно реализована GetHashCode. GetHashCode должна возвращать одно и то же значение для строк, для которых Equals возвращает True.

А, ну да, он же на основе Hashtable! Забавно, но вот так работает:

    public function GetHashCode(s : string):integer;
    begin
      Result := 404;
    end;

Только просадки дикие по производительности, из разряда «как нельзя использовать хеш-таблицу».

Ну, понятно, что GetHashCode приличная сложно будет писаться

Да в принципе можно банально взять для GetHashCode первые три-четыре символа строки, этого будет достаточно, и производительность практически не пострадает. Вот ещё бы научиться оставлять в качестве ключа наиболее встречаемые варианты имени, а не ту форму, которая встретилась первой. Это, видимо, надо сливать все имена в словарь, а потом рассматривать по убыванию частоты появления в тексте, и перегонять в словарик, который выполняет слияние по нашему правилу сравнения. А пока что вот так:

type StringsEqualityComparer = class (IEqualityComparer<string>)
    public function Equals(s1,s2 : string) : boolean;
    begin
      var w := min(s1.Length, s2.Length)-2;
      Result := copy(s1,1,w)=copy(s2,1,w);
    end;
    public function GetHashCode(s : string):integer;
    begin
      Result := copy(s,1,4).GetHashCode;
    end;
end;

begin
  var q := System.Net.WebClient.Create.DownloadString('http://vojnaimir.ru/files/book1.txt').MatchValues('\b\p{Lu}\w{3,}\b');
  var d := new Dictionary<string,integer>(new StringsEqualityComparer());
  foreach var x in q do
    d[x] := d.Get(x) + 1;
  d.OrderByDescending(x->x.Value).Take(20).Print(NewLine);
end.