Как получить ключ из ссылки на хеш-элемент

Предполагать $my_ref = \$hash{'mary'};#my_ref является контрольной точкой для хеш-элемента.
....
позже, как я могу использовать $my_ref получить ключ элемента хеша, на который он указывает? то есть как получить строку "Мэри" из $my_ref?

Я задаю этот вопрос, потому что у меня есть несколько групп списка имен пользователей, некоторые имена пользователей появляются в нескольких группах, которые занимают память. Поэтому я решил создать общий список имен пользователей и позволить этим группам хранить только ссылку на соответствующее имя пользователя, а не имя пользователя.

например, изначально,

%group1 = {'mary'=>1, 'luke'=1,'tom'=1,...}  
%group2 = {'mary'=>1, 'sam'=1,'tom'=1,...}

Здесь вы видите, что "Мэри" и "Том" показаны в обоих group1 а также group2 которые потребляют память. (заметьте, мне все равно значение в этом примере, значение здесь только потому, что структура данных является хешем). Итак, чтобы уменьшить память, я хочу иметь общий список, в котором хранятся все имена пользователей:

%common_hash = {'mary'=>1, 'luke'=1,'tom'=1,'sam'=1...};  
$ref1 = \$common_hash{'mary'};  
$ref2 = \$common_hash{'luke'};  
$ref3 = \$common_hash{'tom'};  
$ref4 = \$common_hash{'sam'};

groups только хранят ссылку на элемент hash:

%group1 = {$ref1=>1, $ref2=1,$ref3=1,...};  
%group2 = {$ref1=>1, $ref4=1,$ref3=1,...}; 

Я думаю, что этот подход может сэкономить много памяти, потому что:

  1. одно имя пользователя сохраняется в памяти один раз, а не несколько раз;
  2. groups хранит ссылку (целое число), а не строку (в моем случае длина каждого имени пользователя составляет в среднем 30 байтов, в то время как каждое целое число составляет всего 4 байта (32-битный sys.) или 8 байтов (64-битный sys.)) (Кстати, поправьте меня, если целое число не использует 4 байта или 8 байтов.)
  3. используя ссылку, я могу сразу получить доступ к имени пользователя, не ища его.

Но как я могу получить имя пользователя из группы?

Если я использую @my_ref = keys %group1Я думаю, что получу значение "Мэри", но не "Мэри".

$result = $($my_ref[0]);

4 ответа

Решение

Извините, хэши не работают таким образом. Вы не экономите память, используя ссылку вместо строки в качестве хеш-ключа, и, кроме того, вы:

  1. затрудняет поиск данных в хэше (скрыт)
  2. мешать внутренней оптимизации хэша в Perl (используя алгоритм хеширования для обеспечения поиска O(1) внутри того, что фактически является списком).

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

Что заставило вас поверить в то, что вы сохраняете память своим кашляющим, новым подходом? Вы запускали профилировщик памяти для разных реализаций?

Как правило, вы не можете получить значение хеша обратно к его ключу (хотя вы можете проходить по хеш-таблице, ища ее линейно, если бы она была уникальной). Если вы хотите отслеживать как хэш-ключ, так и значение, вам нужно сделать это самостоятельно. Некоторые общие подходы:

# iterate through the table by key
foreach my $key (keys %hash)
{
     # here we have both the key and its corresponding value
     print "value at key $key is $hash{$key}\n";
}

# iterate through the table by keys and values
while (my ($key, $value) = each %hash)
{
     print "value at key $key is $value, which is the same as $hash{$key}\n";
}

Пожалуйста, прочитайте о том, как хеши работают в руководстве. Вы также можете прочитать о клавишах и каждой функции.

  1. Ссылка не является целым числом; это SV, так что это будет что-то вроде 24 байтов, а не 4.

  2. Не то, чтобы это имело значение, потому что вы не храните ссылки, потому что хеш-ключи всегда являются строками. Ключи твоего %group1 и т. д. Хэши - это строки, которые выглядят как "HASH(0x19838e2)", что бесполезно.

  3. Не то чтобы это важно, потому что Perl достаточно умен, чтобы не тратить память, если одни и те же строки используются в качестве ключей в нескольких хешах. Правильно, если бы вы просто делали вещи простым, очевидным и разумным способом, Perl использовал бы меньше памяти, чем это делает со сложными вещами, которые вы пытаетесь сделать.

Хеш - это средство связывания имен со скалярами. Если у вас есть хэш и ключ, у вас есть скаляр, а не ссылка на хэш-корзину или что-то в этом роде.

my $value = $hash{name};

Это просто скаляр.

my $ref = \$hash{name};

Это просто ссылка на скаляр. Не более способный содержать информацию, позволяющую вам вернуться к хеш-ключу, чем анонимная ссылка может сказать вам, какое имя может быть в таблице символов или лексической панели (без какой-либо помощи).

Попробуйте думать об этом как таблицы базы данных. Иметь пользовательскую "таблицу" / хеш, которая связывает идентификатор пользователя с информацией о пользователе, а другие хеши используют идентификатор пользователя вместо информации пользователя.

my $userid = 5;
$user->{$groupid};
# would be the hash element for that user with a user id 

Затем вы можете сделать так, чтобы в списках групп использовались цифры вместо имен / имен пользователей.

Тем не менее, я думаю, что вы делаете больше работы для себя, чем необходимо. Вы действительно столкнулись с проблемой с этой программой, использующей слишком много памяти? Наличие дублирующих ключей не является проблемой, если ваши ключи не содержат строки очень большого размера.

Если у вас есть тысяча разных имен пользователей (все 100 символов или меньше) и в совокупности существует 10 000 отношений между пользователем и группой, то у вас есть только:

100 байтов * 10 000 = 1 МБ

И если честно, большинство имен составляют 1/5 от этого размера: 200 КБ

Мое предложение будет беспокоиться об этом, только если у вас много МБ информации (скажем, 500 или более).

Другие вопросы по тегам