在Perl中,如何保存包含子例程引用的哈希?

时间:2012-09-19 20:16:40

标签: perl serialization hashmap

正如标题所说,在Perl中,如何保存包含子例程引用列表的哈希?例如,我有以下哈希,其中包含对其他库中包含的子例程的引用:

my %testMap = (
  helloTest        => \&runHello,
  goodbyeTest      => \&runGoodbye,
);

当我尝试在以下事项中使用Data::Dumper时:

my($out) = new FileHandle ">$fileName"; 
my $serialized => Data::Dumper->Dump([\%testMap], [$HASH_REFERENCE]); 
print $out $serialized; 
close $out;

我最终得到的文件如下所示:

$testMap = {
             'goodbyeTest' => sub { "DUMMY" },
             'helloTest' => sub { "DUMMY" }
           };

当我希望输出看起来像原始列表中的内容时,有没有办法做到这一点?

到目前为止,Data :: Dumper和Storable的一些实验没有发现任何内容,我怀疑它是由于实际的代码不能用于正在运行的代码。

4 个答案:

答案 0 :(得分:5)

自版本2.05以来,

Storable已能够序列化coderef。

use strict;
use warnings;
use Storable;
use Data::Dump 'dump';

{
    no warnings;             # Suppress 'used only once' warning
    $Storable::Deparse = 1;  # Needs to be set to true as per docs
    $Storable::Eval    = 1;       # Same as above
}

sub hello_world { print "Hello world!\n" }

my %hash = (
             helloTest => \&hello_world,
             byeTest   => sub { print "Goodbye!\n" },
           );

store \%hash, 'file';            # Could use freeze/thaw for
my $cloned = retrieve( 'file' ); # in-memory serialization

$cloned->{helloTest}();          # Prints 'Hello world!'

答案 1 :(得分:2)

$Data::Dumper::Deparse设为真值。

这使用B::Deparse从操作码重建源代码。这通常但不总是有效。

$ perl -MData::Dumper -e 'sub foo { print "Hello world" };' \
>     -e  '$Data::Dumper::Deparse=1; print Dumper \&foo'
$VAR1 = sub {
            print 'Hello world';
        };

如果要解析Perl源代码文件并提取子例程的文本,那就是一个非常不同的问题。但there's a package for that也是如此:

# quick and dirty sub extractor
use PPI;
use Data::Dumper;
$doc = PPI::Document->new( "your_source_code_file_name" );
foreach $sub ( @{$doc->find( 'PPI::Statement::Sub' )} ) {
  @t = $sub->tokens;
  $name = $t[2];
  $code = "sub " . join q//, @t[3..$#t];
  $teh_codez{$name} = $code;
}    
print Data::Dumper::Dumper \%teh_codez;

答案 2 :(得分:2)

首先,Data :: Dumper是一个调试工具,而不是一个序列化工具。做后者并不是那么好。至少,请务必设置Purity选项。这将使它在一些不会起作用的情况下起作用。但是,它仍然存在打破别名的问题。

Storable应该用于非平凡的数据,我会使用JSON::XS或YAML模块来处理琐碎的数据。

其次,通过设置Deparse选项可以尝试

use Data::Dumper qw( Dumper );

my $serialised;
{
   local $Data::Dumper::Purity  = 1;
   local $Data::Dumper::Deparse = 1;
   $serialised = Dumper($struct);
}

对于闭包和XS函数将失败,并且它不会捕获有效的pragma。

my $struct = { f => do { my $x = 123; sub { $x } } };

产生

$VAR1 = {
          'f' => sub {
                     $x;
                 }
        };

答案 3 :(得分:1)

我建议您使用要调用的子例程的 names 存储哈希值,然后在检索后将它们解析为子例程引用

此代码显示了这个想法

use strict;
use warnings;

my %testMap = (
  helloTest   => 'runHello',
  goodbyeTest => 'runGoodbye',
);

$testMap{$_} = \&{$testMap{$_}} for keys %testMap;

$testMap{helloTest}();
$testMap{goodbyeTest}();

sub runHello {
  print "runHello\n";
}

sub runGoodbye {
  print "runGoodbye\n";
}