从perl中的eval-ed字符串动态创建subs

sim*_*one 1 perl eval perl-data-structures

我需要将数组结构中的数据结构转换为树状结构.在开始处理数据之前,我知道树的深度,但我希望保持灵活性,以便重新使用代码.

因此,我着眼于动态生成subref(从基于Moose的模块中)到阵列之间的想法.像这样(以简化的方式):

use Data::Dump qw/dump/;

sub create_tree_builder {
     my $depth = shift;
     return eval join '', 'sub { $_[0]->{$_[', 
                           join(']}->{$_[', (1..$depth)),
                          ']} = $_[',  $depth + 1 , '] }'; 
}


my $s = create_tree_builder(5);
my $tree = {};

$s->($tree, qw/one two three four five/, 'a value');

print dump $tree;

# prints
#  {
#     one => { two => { three => { four => { five => "a value" } } } },
#  }
Run Code Online (Sandbox Code Playgroud)

这为我打开了世界,我发现这个eval过程的很酷的用途 - 在参数化生成的字符串中到处都是一个函数(显然,这是一个寻找问题的解决方案).

然而,几乎是真实的感觉有点太好了.

任何意见反对这种做法?还是建议改进?

我可以清楚地看到,评估任意输入可能不是最安全的,但还有什么?

跟进

感谢所有的答案.我使用了amon的代码并进行了一些基准测试,如下所示:

use Benchmark qw(:all) ;

$\ = "\n";

sub create_tree_builder {
 my $depth = shift;
 return eval join '', 'sub { $_[0]->{$_[', 
               join(']}->{$_[', (1..$depth)),
              ']} = $_[',  $depth + 1 , '] }'; 
}


my $s = create_tree_builder(5);

$t = sub {
$_[0] //= {};

    my ($tree, @keys) = @_;
    my $value = pop @keys;

    $tree = $tree->{shift @keys} //= {} while @keys > 1;
    $tree->{$keys[0]} = $value;
};


cmpthese(900000, {
        'eval'  => sub { $s->($tree, qw/one two three four five/, 'a value') },
    'build' => sub { $t->($tree, qw/one two three four five/, 'a value') },

});
Run Code Online (Sandbox Code Playgroud)

结果显然有利于建造树,而不是评估工厂:

            Rate build  eval
build  326087/s    --  -79%
eval  1525424/s  368%    -- 
Run Code Online (Sandbox Code Playgroud)

我承认我以前可以做到这一点.我将尝试使用更多随机树(而不是一遍又一遍地分配相同的元素),但我认为没有理由说结果应该是不同的.

非常感谢您的帮助.

Bor*_*din 5

编写通用子例程来构建这样的嵌套哈希非常容易.这种方式比编写一个能为特定数量的哈希级别生成这样一个子程序的工厂简单得多.

use strict;
use warnings;

sub tree_assign {

  # Create an empty tree if one was not given, using an alias to the original argument
  $_[0] //= {};

  my ($tree, @keys) = @_;
  my $value = pop @keys;

  $tree = $tree->{shift @keys} //= {} while @keys > 1;
  $tree->{$keys[0]} = $value;
}

tree_assign(my $tree, qw/one two three four five/, 'a value');

use Data::Dump;
dd $tree;
Run Code Online (Sandbox Code Playgroud)

产量

{
  one => { two => { three => { four => { five => "a value" } } } },
}
Run Code Online (Sandbox Code Playgroud)