Указатель на общий тип

В процессе преобразования данной эффективной реализации хеш-карты на основе указателей в общую реализацию хэш-карты я наткнулся на следующую проблему:

У меня есть класс, представляющий хэш-узел (реализация хеш-карты использует двоичное дерево)

THashNode <KEY_TYPE, VALUE_TYPE> = class
public
  Key      : KEY_TYPE;
  Value    : VALUE_TYPE;
  Left     : THashNode <KEY_TYPE, VALUE_TYPE>;
  Right    : THashNode <KEY_TYPE, VALUE_TYPE>;
end;

В дополнение к этому есть функция, которая должна возвращать указатель на хеш-узел. я хотел написать

PHashNode = ^THashNode <KEY_TYPE, VALUE_TYPE>

но это не компилируется (";" ожидается, но "‹" найдено).

Как я могу иметь указатель на общий тип?

И адресовано Барри Келли: если вы читаете это: да, это основано на вашей реализации хеш-карты. Вы сами не написали такую ​​общую версию своей реализации, не так ли? Это сэкономило бы мне время :)


person jpfollenius    schedule 27.04.2009    source источник


Ответы (4)


Извини, Смашер. Указатели на открытые универсальные типы не поддерживаются, поскольку универсальные типы указателей не поддерживаются, хотя их можно (ошибка компилятора) создать при определенных обстоятельствах (в частности, указатели на вложенные типы внутри универсального типа); эта «функция» не может быть удалена в обновлении, если мы сломаем чей-то код. Ограничение на универсальные типы указателей должно быть снято в будущем, но я не могу обещать, когда именно.

Если речь идет о типе, указанном в JclStrHashMap, который я написал (или о древнем модуле HashList), ну, самый простой способ воспроизвести его — изменить тип узла на класс и передать любые двойные указатели как Pointer с соответствующими Кастинг. Однако, если бы я снова писал этот модуль сегодня, я бы не стал реализовывать сегменты как бинарные деревья. У меня появилась возможность написать словарь в модуле Generics.Collections, хотя со всеми остальными компиляторами Delphi времени было слишком мало перед поставкой для надежного контроля качества, а сама поддержка общих функций находилась в постоянном движении до довольно позднего времени.

Я бы предпочел реализовать сегменты хэш-карты как один из динамических массивов с двойным хэшированием для каждого сегмента или связанных списков ячеек из непрерывного массива, в зависимости от того, что лучше всего получается в тестах с использованием репрезентативных данных. Логика заключается в том, что стоимость промаха кеша следующих ссылок в дереве/списке должна доминировать над любой разницей в поиске корзины между деревом и списком с хорошей хэш-функцией. Текущий словарь реализован как прямое линейное зондирование прежде всего потому, что его было относительно легко реализовать и он работал с доступным набором примитивных универсальных операций.

Тем не менее, сегменты бинарного дерева должны были стать эффективной защитой от плохих хеш-функций; если бы они были сбалансированными бинарными деревьями (=> стоимость модификации даже больше), они были бы в среднем O(1) и O(log n) в худшем случае.

person Barry Kelly    schedule 27.04.2009
comment
+1 Спасибо за подробные комментарии! Я говорил о модуле HashList (нашел его где-то на форуме codegear). Я проведу несколько тестов, касающихся эффективности реализации двоичного дерева по сравнению с реализацией динамического массива для моего сценария использования. Или, возможно, я просто подожду обновления, касающегося TDictionary. - person jpfollenius; 27.04.2009
comment
Я реализовал свой класс списка хэшей, используя динамические массивы для списков сегментов, как вы предложили. Пока размер хеша выбран разумно, производительность остается такой же хорошей, как и раньше (удаление еще лучше по очевидным причинам). Спасибо за помощь! - person jpfollenius; 28.04.2009

Чтобы на самом деле ответить на ваш вопрос, вы не можете сделать указатель на общий тип, потому что «универсальные типы» не существуют. Вы должны сделать указатель на определенный тип с заполненными параметрами типа.

К сожалению, компилятор не любит находить угловые скобки после ^. Но он примет следующее:

   TGeneric<T> = record
      value: T;
   end;

   TSpecific = TGeneric<string>;

   PGeneric = ^TSpecific;

Но "PGeneric = ^TGeneric<string>;" выдает ошибку компилятора. Мне кажется глюк. Я бы сообщил об этом в КК на вашем месте.

Почему вы вообще пытаетесь сделать указатель на объект? Объекты Delphi относятся к ссылочному типу, поэтому они уже являются указателями. Вы можете просто привести ссылку на свой объект к Pointer, и все в порядке.

person Mason Wheeler    schedule 27.04.2009
comment
Даже не нужно приводить к указателю. Это уже совместимо по назначению. - person Rob Kennedy; 27.04.2009
comment
Для необходимости указателя: см. мой комментарий к ответу Роба. Возможно, я смогу этого избежать, но я просто переводил PPHashNode в исходный код... - person jpfollenius; 27.04.2009

Если бы Delphi вообще поддерживала общие типы указателей, это должно было бы выглядеть так:

type
  PHashNode<K, V> = ^THashNode<K, V>;

То есть укажите универсальные параметры слева, где вы объявляете имя типа, а затем используйте эти параметры при построении типа справа.

Однако Delphi не поддерживает это. См. КК 66584.

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

person Rob Kennedy    schedule 27.04.2009
comment
Он используется для функции FindNode (const Key: KEY_TYPE): PHashNode, так что можно записать Node := FindNode (Key); Узел := THashNode.Create; В исходной версии это был двойной указатель PPHashNode. - person jpfollenius; 27.04.2009

В модуле Generics.Collections есть универсальная хэш-карта под названием TDictionary. К сожалению, на данный момент он сильно сломан, но, по-видимому, это будет исправлено в обновлении № 3, которое должно выйти в течение нескольких дней, по словам Ника Ходжеса.

person Mason Wheeler    schedule 27.04.2009
comment
Я знаю об этом классе, но хорошая вещь в реализации, которую я использую (и которую я хочу преобразовать), заключается в том, что она использует двоичные деревья для хранения списков сегментов, что делает хранение больших списков очень эффективным. Насколько я вижу из исходного кода, TDictionary просто использует динамические массивы для списков сегментов. - person jpfollenius; 27.04.2009