如何将参数传递给使用eval定义的Perl子例程?

4

我正在使用一个配置文件(YAML格式)来定义类型,这些类型稍后用于验证应用程序所需的其他配置值:

---
action: >
        use List::MoreUtils;
        my $value = $_;
        any { $value eq $_ } qw(fatal keep merge non-fatal replace);
dir   : return defined $_ ? -d $_ : -1;
file  : return defined $_ ? -f $_ : -1;
string: 1;


---
config-element:
    value: foo
    type : file
etc ...

这个想法是对每个类型的定义进行eval,将它们扔进哈希表中,然后调用以验证配置数据(以下是为了易于理解而简化的示意图):

#throw sub refs into hash
my %type_sub;
foreach my $key (keys %$type_def_ref) {
    my $sub_str = "sub {$type_def_ref->{$key}}";
    $type_sub{$key} = eval $sub_str;

}

#validate (myfile is a real file in the cwd)
print $type_sub{file}->('myfile'),"\n";
print $type_sub{action}->('fatal'), "\n";

问题在于%type_sub中的子程序似乎不接受参数。在上述情况下,第一条打印语句输出-1,而第二条则输出:
Use of uninitialized value $value in string eq at (eval 15) line 1.
Use of uninitialized value $_ in string eq at (eval 15) line 1.
Can't call method "any" without a package or object reference at 
(eval 15) line 1.

这并不是我预期的结果,但是子程序正在被调用。

我做错了什么?

编辑:我太草率了,现在一切都正常了。谢谢Friedo。

2个回答

5
不要在配置文件中编写代码。创建一个包含代码的库,仅配置要使用的子例程名称即可。这可以节省大量将字符串翻译为代码并管理过程的工作。当有人调整配置并引入语法错误时,它还可以节省大量跟踪问题的时间。
我在精通Perl的“配置”章节以及关于动态子例程的章节中广泛讨论了这个问题。
代码不应出现在配置中。直到你相信为止,请坚持这一点。

当然,你是对的,但我认为一行代码并不是什么大问题(而且我已经违反了这个原则)。 - gvkv
另一个问题是我不知道如何在不关闭严格引用的情况下完成这个任务。 - gvkv

3
您的子程序参数将在@_数组中,而不是$_。要获取第一个参数,请查看$_[0]或执行my $foo = shift;。(shift默认情况下对@_进行操作。)
至于any,我认为问题是由于any无法在运行时加载其原型(子例程原型只能在编译时调用)。您可能需要使用显式括号和显式子例程引用:
any( sub { $value eq $_ }, qw(fatal keep merge non-fatal replace) );

哇。我一直使用@_与shift或列表赋值,并且由于$_很常见,我完全忘记它不会自动设置。 - gvkv
我刚刚尝试了你的“any”解决方案,但没有成功。这里有一个更一般性的问题,如何使用eval或其他方法定义带有字符串的子程序。也许我稍后会问这个问题。 - gvkv
我已经修复了。List::MoreUtils默认情况下不导出任何内容。 - gvkv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接