2016-04-20 3 views
0

Я борюсь с доступом/изменением хэшей неизвестной (т. Е. Динамической) глубины.Perl: Хэш доступа с динамической глубиной

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

#      #Length Width Height Results 
my %results = (  
         '2' => {   
           '3' => {   
             '7' => { 
               'Area' => 6, 
               'Volume' => 42, 
               }, 
             }, 
           }, 
         '6' => {   
           '4' => {   
             '2' => { 
               'Area' => 24, 
               'Volume' => 48, 
               }, 
             }, 
           }, 
         ); 

Я понимаю, как получить доступ к отдельный элемент в хеше, например $results{2}{3}{7}{'Area'} дал бы мне 6, или я мог проверить, была ли эта комбинация измерений найдена во входном файле с exists $results{2}{3}{7}{'Area'}. Однако эта нотация с рядом {} брекетов предполагает, что я знаю при написании кода, что будет 4 слоя ключей.

Что делать, если есть более или менее, и я обнаружил это только во время выполнения? Например. если в файле были только длина и ширина, как бы вы создали код, который затем будет обращаться к хешу, например, $results{2}{3}{'Area'}?

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

Я почти хочу нотации, как:

my @hashkeys = (2,3,7); 

if exists ($hash{join("->",@hashkeys)}){ 
    print "Found it!\n"; 
} 

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

Надеюсь, этого достаточно, чтобы понять мой вопрос, но я могу попробовать работать с MWE, если нет.

Спасибо.

+4

Это не похоже на отличную структуру данных. Во-первых, вы не можете иметь два элемента с одинаковыми размерами, так как хеш-ключи уникальны. Использование плоской структуры с ключами 'length',' width' и 'height' будет иметь больший смысл. – ThisSuitIsBlackNot

+0

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

+0

Это настоящие данные?Или вы даете данные заполнителя, или это домашнее задание? Поскольку [@ThisSuitIsBlackNot говорит выше] (http://stackoverflow.com/questions/36752179/perl-access-hash-of-dynamic-depth#comment61084783_36752179) Я не понимаю, почему вы организовали такие вещи в первом место. Как вы видели, это проблематично. Похоже, у вас есть список * вещей * с длиной, шириной, высотой, областью и томом, некоторые из которых могут отсутствовать, и я бы структурировал его точно так же: как массив массивов из пяти элементов. Если это зависит от того, что ваше приложение делает, как вы могли бы получить доступ к определенным записям быстрее – Borodin

ответ

4

Так вот рекурсивная функция, которая делает более или менее то, что вы хотите:

sub fetch { 
    my $ref = shift; 
    my $key = shift; 
    my @remaining_path = @_; 

    return undef unless ref $ref; 
    return undef unless defined $ref->{$key}; 
    return $ref->{$key} unless scalar @remaining_path; 
    return fetch($ref->{$key}, @remaining_path); 
} 

fetch(\%results, 2, 3, 7, 'Volume'); # 42 
fetch(\%results, 2, 3);    # hashref 
fetch(\%results, 2, 3, 7, 'Area', 8); # undef 
fetch(\%results, 2, 3, 8, 'Area'); # undef 

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

+0

Я отмечаю это как asnwer, так как он действительно ответил на мой вопрос, но для кого-то другого, рассматривающего подобный подход, сначала см. Комментарии ниже моего вопроса. В конце концов, я изменил свой подход, поскольку, как обсуждалось выше, он с самого начала был продумано. Благодарю. – SSilk

1

Посмотрите на $; в "man perlvar".

http://perldoc.perl.org/perlvar.html#%24%3b

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

my %foo; 
my (@KEYS)=(2,3,7); 
$foo{ join($; , @KEYS) }{Area}=6; 
$foo{ join($; , @KEYS) }{Volume}=42; 
+0

Точка '$;' заключается в том, что она волшебным образом превратит '$ foo {@keys}' в '$ foo {join $ ;, @keys}'. Если вы сделаете это вручную, вы можете использовать разделитель легче на глазу и более точно подбирать данные типа ','. – Schwern