Как получить ключ из ссылки на хеш-элемент
Предполагать $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,...};
Я думаю, что этот подход может сэкономить много памяти, потому что:
- одно имя пользователя сохраняется в памяти один раз, а не несколько раз;
- groups хранит ссылку (целое число), а не строку (в моем случае длина каждого имени пользователя составляет в среднем 30 байтов, в то время как каждое целое число составляет всего 4 байта (32-битный sys.) или 8 байтов (64-битный sys.)) (Кстати, поправьте меня, если целое число не использует 4 байта или 8 байтов.)
- используя ссылку, я могу сразу получить доступ к имени пользователя, не ища его.
Но как я могу получить имя пользователя из группы?
Если я использую @my_ref = keys %group1
Я думаю, что получу значение "Мэри", но не "Мэри".
$result = $($my_ref[0]);
4 ответа
Извините, хэши не работают таким образом. Вы не экономите память, используя ссылку вместо строки в качестве хеш-ключа, и, кроме того, вы:
- затрудняет поиск данных в хэше (скрыт)
- мешать внутренней оптимизации хэша в 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";
}
Пожалуйста, прочитайте о том, как хеши работают в руководстве. Вы также можете прочитать о клавишах и каждой функции.
Ссылка не является целым числом; это SV, так что это будет что-то вроде 24 байтов, а не 4.
Не то, чтобы это имело значение, потому что вы не храните ссылки, потому что хеш-ключи всегда являются строками. Ключи твоего
%group1
и т. д. Хэши - это строки, которые выглядят как "HASH(0x19838e2)", что бесполезно.Не то чтобы это важно, потому что 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 или более).