2

As the title says, in Perl, how can I save a hash that contains a list of subroutine references? For example, I have the following hash that contains the references to the subroutines which are contained in other libraries:

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

When I try to use Data::Dumper in the following matter:

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

I end up with a file that looks like the following:

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

When I would like the output to look like what appears in the original listing, is there a way to do this?

Some experimentation with Data::Dumper and Storable have so far turned up nothing and I suspect that it is due to the actual code for the references not being available to the code that is running.

4

4 回答 4

5

Storable从 2.05 版开始就能够序列化 coderefs。

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!'
于 2012-09-19T20:20:06.760 回答
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 源代码文件并提取子例程的文本,这是一个非常不同的问题。但是也有一个包

# 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;
于 2012-09-19T20:29:12.933 回答
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 函数,它将失败,并且它不会捕获有效的编译指示。

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

生产

$VAR1 = {
          'f' => sub {
                     $x;
                 }
        };
于 2012-09-19T20:40:14.503 回答
1

我建议您将哈希与要调用的子程序的名称一起存储,然后在检索后将它们解析为子程序引用

这段代码显示了这个想法

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";
}
于 2012-09-20T00:14:40.053 回答