Как сделать то же самое, что и ссылка, используя typeglob в Perl? - PullRequest
4 голосов
/ 28 июня 2011
$ref = \%hash;
$ref = \@hash;

Как сделать то же самое, что и ссылка, используя typeglob в perl?

Какие точные шаги делает Perl для интерпретации $$ref{key}?

Ответы [ 4 ]

9 голосов
/ 28 июня 2011

Используйте синтаксис *foo{THING}, который описан в разделе Создание ссылок документации perlref.

Ссылка может быть создана с использованием специального синтаксиса, который с любовью называют синтаксисом *foo{THING}. *foo{THING} возвращает ссылку на слот THING в *foo (это запись таблицы символов, которая содержит все, что известно как foo).

$scalarref = *foo{SCALAR};
$arrayref  = *ARGV{ARRAY};
$hashref   = *ENV{HASH};
$coderef   = *handler{CODE};
$ioref     = *STDIN{IO};
$globref   = *foo{GLOB};
$formatref = *foo{FORMAT};

Например:

#! /usr/bin/env perl

use strict;
use warnings;

our %hash = (Ralph => "Kramden", Ed => "Norton");
our @hash = qw/ apple orange banana cherry kiwi /;

my $ref;

$ref = *hash{HASH};
print $ref->{Ed}, "\n";

$ref = *hash{ARRAY};
print $ref->[1], "\n";

Выход:

Norton
orange

Что касается второй части вашего вопроса, добавьте

print $$ref{Ralph}, "\n";

после того, как Эд выдаст ожидаемый результат. Компилятор генерирует код для этой строки, который проходит в следующей последовательности:

  1. Извлечь запись пэда для $ref.
  2. Получите $ref штучку.
  3. Найдите ключ в хэше из шага 2.

Но не верьте мне на слово. Чтобы сократить объем производства, рассмотрим аналогичный двухслойный:

my $ref = { Ralph => "Kramden" };
print $$ref{Ralph};

Запуск его с помощью perl, скомпилированного для отладки, дает нам

$ debugperl -Dtls ref 
[...]
(ref:1) nextstate
    =>  
(ref:2) pushmark
    =>  *  
(ref:2) padsv($ref)              # STEP 1
    =>  *  \HV()  
(ref:2) rv2hv                    # STEP 2
    =>  *  HV()  
(ref:2) const(PV("Ralph"\0))     # STEP 3a
    =>  *  HV()  PV("Ralph"\0)  
(ref:2) helem                    # STEP 3b
    =>  *  PV("Kramden"\0)  
(ref:2) print
    =>  SV_YES  
(ref:2) leave
[...]

Обратите внимание, что для глобалов это немного отличается.

Я не уверен, каково ваше большее намерение, но есть несколько важных предостережений. Обратите внимание, что typeglob представляет запись в таблице символов 1045 *, поэтому вы не можете получить лексические выражения таким образом, потому что они живут в пэдах, а не в таблице символов. Например, допустим, вы вставили my @hash = ("splat"); непосредственно перед присвоением $ref в приведенном выше коде. Результат может вас удивить.

$ ./prog 
"my" variable @hash masks earlier declaration in same scope at ./prog line 11.
Norton
orange

Поведение по отношению к скалярам также может быть удивительным.

*foo{THING} возвращает undef, если эта конкретная вещь еще не использовалась, за исключением скаляров. *foo{SCALAR} возвращает ссылку на анонимный скаляр, если $foo еще не использовался. Это может измениться в будущем выпуске.

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

6 голосов
/ 28 июня 2011

Если вы спрашиваете, как получить ссылку на глобус типа, это просто:

my $ref = \*symbol_name_here;

Для «буквального имени» их символа (именно там вы вводите точное имясимвола), а не переменная.Но вы можете сделать это:

my $ref = Symbol::qualify_to_ref( $symbol_name );

для переменного символа.Однако вышеприведенное работает с strict, а более простое из приведенного ниже - нет:

my $ref = \*{$symbol_name};

Одна из приятных особенностей Symbol::qualify* заключается в том, что он обрабатывает имена пакетов каквторая переменная.Итак ...

my $sref = Symbol::qualify_to_ref( $symbol_name, $some_other_package );

делает то же самое, что и \*{$some_other_package.'::'.$symbol_name} и работает с strict.

Как только у вас есть символ ref, чтобы получить слот, вы должныПочтите ссылку, поэтому Perl не думает, что вы пытаетесь использовать ее как хеш, например.

my $href  = *{ $sref }{HASH};
my $code  = *{ $sref }{CODE};
my $arref = *{ $sref }{ARRAY};
my $io    = *{ $sref }{IO};

Еще один дубль

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

Либо

*hash{HASH}->{key}

, либо

${ *hash{HASH} }{key}

будет работать.Это безопаснее, хотя

( *hash{HASH} || {} )->{key};
${ *hash{HASH} || {} }{key};

Если вы хотите сделать это не с прямой записью, а со ссылкой на таблицу, вы должны сделать следующее:

my $ref   = \*hash;
my $value = *{ $ref }{HASH} && *{ $ref }{HASH}->{key};

ПРИМЕЧАНИЕ: %hash обязательно должна быть переменной пакета.В таблице символов находятся только переменные пакета (поэтому в современных таблицах символов обычно присутствуют только переменные sub и @ISA и переменные Exporter).Лексические переменные (объявленные my) находятся в "pad" .


ОБНОВЛЕНИЕ:

  • Я так часто отказывался от использования Symbol.Любопытно, что, хотя это core , кажется, нестандартное в том смысле, в каком Perlers делает - и видит - вещи.Вместо этого я использую прямой путь в том, что я называю «без блоков», настолько локализованным, насколько я могу их сделать.

    # All I want is one symbol. 
    # Turn strict-refs off, get it, turn it back on.
    my $symb = do { no strict 'refs'; \*{$symbol_name} };
    

    ИЛИ

    {   no strict 'refs';
        *{$package_name.'::'.$symbol_name} = $sub_I_created;
        ... # more reckless symbol table mucking...
    }
    
  • Я почти всегда использую идиому *STDERR{IO} для ссылки на дескриптор файла глоба.В современном Perl это обычно объектов .

    my $fh = *STDERR{IO};
    say blessed( $fh ); # IO::File
    $fh->say( 'Some stuff' );
    
1 голос
/ 28 июня 2011

Я не думаю, что вы можете сделать то же самое с typeglobs, как вы можете сделать со ссылками (например, лексические переменные никогда не включают глобус типа). Чего ты хочешь достичь в итоге?

Что касается $$ref{key}, Perl работает следующим образом:

  • первый $ сообщает, что возвращен скаляр
  • наличие переменной после сигилы указывает, что эта переменная должна иметь ссылку
  • {...} указывает, что $ref должен быть ссылкой на хеш
0 голосов
/ 28 июня 2011

Perl замечательно и классно экспериментален:

Попробуйте:

 perl -MO=Terse -e' $ref = \%hash; $ret = $$ref{key}'

или:

 perl -MO=Debug -e' $ref = \%hash; $ret = $$ref{key}'

и все будет раскрыто.Если у вас есть отладочный Perl, вы даже можете сделать:

 perl -Dt -e'$ref = \%hash; $ret = $$ref{key}'
...