Разделить хэш на хэш массива

У меня есть три хэша, каждый из которых я хотел бы взорвать в хэш массивов. Каждый из них может быть передан в подпрограмму с разделяемой функцией, но для этого требуется, чтобы одна и та же подпрограмма вызывалась 3 раза. Я пытался перебрать каждый хэш, и они разделили его, но безуспешно. Можно ли это сделать без вызова подпрограммы? Код, который я пробовал:

#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

my %hash1 = (
    A => "AAAAAAAAAA",
    B => "AAAAANAAAA",
    C => "AAAAAXAAAA",
    D => "AAXAAAAAAA",
    E => "AAAAAAAAAX",
);

my %hash2 = (
    F => "BBBBBBBBBB",
    G => "BBBBBBBBBB",
    H => "BBXBBBBBBB",
    I => "BBBBBBBBBB",
    J => "AAAAANAAAA",
);

foreach my $ref ( \%hash1, \%hash2 ) {
    while ( my ($key, $value) = each %$ref) {
        @{ $ref->{$key} } = split (//, $value );
    }
}

print Dumper %hash1;

но выдает предупреждение:

Can't use string ("AAAAAAAAAA") as an ARRAY ref while "strict refs" in use

Заранее спасибо.


person PedroA    schedule 05.08.2014    source источник


Ответы (2)


Ваша ошибка в том, что вы берете значение ключа $ref->{$key}, равное AAAAAAA и т. д., и используете его в качестве ссылки с фигурными скобками @{ ... }. Вам нужно назначить $ref->{$key} напрямую.

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

foreach my $ref ( \%hash1, \%hash2 ) {
    while ( my ($key, $value) = each %$ref) {
        $ref->{$key} = [ split (//, $value ) ];  # using anonymous array
        # my @list = split (//, $value );
        # $ref->{$key} = \@list;                 # using named array
    }
}

Этот процесс перезапишет исходные значения, но я предполагаю, что это то, что вам нужно.

Более прямой способ добиться того же:

$_ = [ split // ] for values %hash1, values %hash2;

Здесь мы (ab) используем тот факт, что элементы в цикле for имеют псевдоним исходных переменных, и просто перезаписываем значения так же, как мы делали выше.

Если сжатый формат нежелателен, более подробной альтернативой может быть:

for my $value (values %hash1, values %hash2) {
    $value = [ split //, $value ];
}
person TLP    schedule 05.08.2014
comment
+1 Хорошая острота! Просто из любопытства, под более быстрым вы подразумеваете время выполнения или время кодирования? - person NigoroJr; 05.08.2014
comment
Помимо решения, у него также есть хорошие объяснения. Большое спасибо. - person PedroA; 05.08.2014
comment
Согласен, таким людям, как я, которые все еще изучают perl, становится намного легче. Спасибо @TLP. - person jaypal singh; 05.08.2014
comment
@NigoroJr Ах, это был плохой способ выразить это .. Я имел в виду меньше печатать и больше по делу. - person TLP; 05.08.2014
comment
@TLP Спасибо, сэкономил мне время, чтобы написать тест :) - person NigoroJr; 05.08.2014
comment
Примечание. Код можно записать как $_ = [ split // ] for %hash1, %hash2;. Однако это нехорошо, поскольку используется тот факт, что хеш-ключи нельзя изменить. Вместо этого следует использовать вызов values перед каждым хешем, чтобы сообщить о намерении. - person Miller; 06.08.2014
comment
@Miller Я не писал код, который, как вы подразумевали, написал я. Я написал $_ = [ split // ] for values %hash1, %hash2; Это в истории изменений, если вы не уверены. - person TLP; 06.08.2014
comment
@TLP Я знаю. В этой строке values работает с %hash1, но цикл for фактически выполняет итерацию по ключам и значениям %hash2. Просто случилось так, что Perl не выдает ошибку при попытке изменить ключи, хотя, вероятно, должен. - person Miller; 06.08.2014

Предполагая, что я правильно понял, что вы хотите сделать, вы можете использовать нарезку хеша и сделать что-то вроде это:

#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

my %hash1 = (
    A => "AAAAAAAAAA",
    B => "AAAAANAAAA",
    C => "AAAAAXAAAA",
    D => "AAXAAAAAAA",
    E => "AAAAAAAAAX",
);

my %hash2 = (
    F => "BBBBBBBBBB",
    G => "BBBBBBBBBB",
    H => "BBXBBBBBBB",
    I => "BBBBBBBBBB",
    J => "AAAAANAAAA",
);

foreach my $ref ( \%hash1, \%hash2 ) {
    my @keys = keys %$ref;
    @$ref{@keys} = map { [split //, $ref->{$_}] } @keys;
}

print Dumper \%hash1;
print Dumper \%hash2;

Это как писать:

foreach my $ref ( \%hash1, \%hash2 ) {
    my @keys = keys %$ref;
    foreach my $key (@keys) {
        my @arr = split //, $ref->{$key};
        $ref->{$key} = \@arr;
    }
}

просто проще.

person NigoroJr    schedule 05.08.2014