Работа с хэшами в Perl

Работа с хэшами в Perl

Работа с хэшами в Perl

Автор статьи не дает никаких гарантий, что код приведенный в ней будучи «преобразованный» с помощью Copy/Past заработает.
Все примеры работали на момент написания статьи.

Перевод достаточно вольный. Комментарии и предложения оставляйте в виде комментариев, на www.codenet.ru

Alex BATKO
Оригинал (англ.): http://www.cs.mcgill.ca/~abatko/
<abatko AT cs.mcgill.ca>


Инициализация (очистка) хэша

Самый быстрый способ очистки — это присвоение пустого списка.

Реализация

    my %hash = ();

Примечание

Часто спрашивают, как инициализировать указатель на хэш (hash ref).
Указатель — это скалярная переменная и инициализируется она соответствующим
образом. Например:

    my $hash_ref;
    my $hash_ref = 0;  # zero

Добавление пары ключ/значение в хэш

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

Хэш:

Решение

    $hash{ 'key' } = 'value';    # хэш

    $hash{ $key } = $value;      # хэш, с использованием переменной

Указатель на хэш:

Решение

    $href->{ 'key' } = 'value';  # указатель на хэш

    $href->{ $key } = $value;    # указатель на хэш, с использованием переменной

Добавление нескольких пар ключ/значение в хэш

Решение

Эти операции эквивалентны, просто второй более читаем.

    %hash = ( 'key1', 'value1', 'key2', 'value2', 'key3', 'value3' );

    %hash = (
        key1 => 'value1',
        key2 => 'value2',
        key3 => 'value3',
    );

Копирование хэшей

Решение

    my %hash_copy = %hash;  # копирование хэша

    my $href_copy = $href;  # копирование указателя на хэш

Удаление одной пары ключ/значение

Не смотря на то что удаление хэша и удаление указателя на хэш, это разные операция,
обе они выполняются с помощью функции delete.

Решение

Хэш:

    delete $hash{$key};

Указатель на хэш:

    delete $hash_ref->{$key};

Перебор всех пар ключ/значение

Пример, приведенный ниже, печатает все пары ключ/значение.

Решение

Использование функции each с циклом while. Обратите внимание,
что each переберет пары в случайном порядке, но порядок будет совпадать с перебором
с помощью функций keys и values.

    while ( my ($key, $value) = each(%hash) ) {
        print "$key => $value\n";
    }

Для указатель на хэш небольшое отличие:

    while ( my ($key, $value) = each(%$hash_ref) ) {
        print "$key => $value\n";
    }

Решение

Использование функции keys с циклом for

    for my $key ( keys %hash ) {
        my $value = $hash{$key};
        print "$key => $value\n";
    }

Пример

    my $file = $ARGV[0] || "-";

    my %from = ();

    open FILE, "< $file" or die "Can't open $file : $!";

    while( <FILE> ) {
        if (/^From: (.*)/) { $from{$1}++ }  # считаем повторение отправителя
    }

    close FILE;

    for my $sender ( sort keys %from ) {
        print "$sender: $from{$sender}\n";
    }

Получение размера хэша

Решение

    print "size of hash:  " . keys( %hash ) . ".\n";

Solution

    my $i = 0;

    $i += scalar keys %$hash_ref;  # метод 1: явный скалярный контекст
    $i += keys %$hash_ref;         # метод 2: неявный скалярный контекст

Использование указателей на хэш

Решение

    sub foo
    {
        my $hash_ref;

        $hash_ref->{ 'key1' } = 'value1';
        $hash_ref->{ 'key2' } = 'value2';
        $hash_ref->{ 'key3' } = 'value3';

        return $hash_ref;
    }

    my $hash_ref = foo();

    print "ключи... ", sort keys %$hash_ref, "...\n";

Функция строящая хэш из хэшей и возвращающая указатель на хэш

Решение

    sub foo
    {
        my ( $login, $p, $uid, $gid, $gecos, $dir, $s );

        my %HoH = ();

        my $file = '/etc/passwd';
        open( PASSWD, "< $file" ) or die "Can't open $file : $!";

        while( <PASSWD> ) {
            ( $login, $p, $uid, $gid, $gecos, $dir, $s ) = split( ':' );

            $HoH{ $login }{ 'uid' } = $uid;
            $HoH{ $login }{ 'gid' } = $gid;
            $HoH{ $login }{ 'dir' } = $dir;
        }

        close PASSWD;

        return \%HoH;
    }

Доступ к хэшу из хэшей с помощью указателей. Вывод значений

Решение

    my $rHoH = foo();

    my( $uid, $gid, $dir );

    for my $login ( keys %$rHoH ) {

        $uid =       $rHoH->{ $login }->{ 'uid' };   # метод 1. Более читабельный
        $gid =    ${ $rHoH->{ $login } }{ 'gid' };   # метод 2
        $dir = ${ ${ $rHoH }{ $login } }{ 'dir' };   # метод 3. Менее читабельный

        print "uid: $uid, gid: $gid, dir, $dir.\n";
    }

Решение

    my $rHoH = foo();

    for my $k1 ( sort keys %$rHoH ) {
        print "k1: $k1\n";
        for my $k2 ( keys %{$rHoH->{ $k1 }} ) {
            print "k2: $k2 $rHoH->{ $k1 }{ $k2 }\n";
        }
    }

Функция строящая хэш из хешей из хешей и возвращающая указатель на хэш

Решение

    sub foo
    {
        my %HoHoH = ();

        while( ... ) {

            if( /LOCATION:/ ) {

                ...

            } elsif( /MODULE:/ ) {

                $HoHoH{ $loc }{ $module_type }{ MODULE_NAME } = $module_name;

            } elsif( $ARGS_ALLOWED ) {

                $HoHoH{ $loc }{ $module_type }{ $arg_name } = $arg_value;

            }

        }

        return \%HoHoH;
    }

Доступ к хэшу из хэшей из хэшей с помощью указателей. Вывод значений.

Решение

    my $rHoH = foo();

    for my $k1 ( sort keys %$rHoHoH ) {
        print "$k1\n";

        for my $k2 ( sort keys %{$rHoHoH->{ $k1 }} ) {
            print "\t$k2\n";

            for my $k3 ( sort keys %{$rHoHoH->{ $k1 }->{ $k2 }} ) {
                print "\t\t$k3 => $rHoHoH->{ $k1 }->{ $k2 }->{ $k3 }\n";
            }
        }
    }

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

Решение

    while( my ($k, $v) = each %$hash_ref ) {
        print "ключ: $k, значение: $v.\n";
    }

Определение существования значения в хэше

Решение

    print "Значение СУЩЕСТВУЕТ, но может быть не определено.\n"
	if exists  $hash{ $key };

    print "Значение ОПРЕДЕЛЕНО, но может быть false.\n"
	if defined $hash{ $key };

    print "Значение TRUE\n"
	if $hash{ $key };

Пример

Допустим, мы выполнили SQL запрос, который может вернуть записи, содержащие значение NULL.

Перед тем как использовать результат запроса нам необходимо проверить, ОПРЕДЕЛЕНЫ ли полученные значения.

Обратите внимание, функция sql_fetch_hashref() соединяется с сервером баз данных, подготавливает запрос,
выполняет его и получает указатель на хэш с помощью DBI функции fetchrow_hashref().

    my $answers = 'a,b,c,d,e';

    my $sql = "select max_time, $answers from questions " .
              'where question_number=?';
    my $hash_ref = sql_fetch_hashref( $sql, $q );

    my @answers = split ',', $answers;

    my $max_time = $hash_ref->{max_time} || '60';

    my $hash_ref_ans;
    for my $letter ( @answers ) {
        $hash_ref_ans->{ $letter } = $hash_ref->{ $letter }
            if defined $hash_ref->{ $letter };
    }

Цикл for создает хэш только с ОПРЕДЕЛЕННЫМ парами ключ/значение.

Автор

Alex BATKO <abatko AT cs.mcgill.ca>

Спасибо всем, кто присылал комментарии и предложения.

См. еще

http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/

Комментарии