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

问题描述:

我使用一个配置文件(YAML),以定义以后使用的类型来验证我的应用程序所需的其他配置值:如何将参数传递给使用eval定义的Perl子例程?

--- 
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. 

这是不是在所有我所期望的,但在子程序调用。

我在做什么错?

编辑: 我正在马虎,现在一切正常。感谢弗里多。

您的子程序参数将在@_数组中,而不是$_。要获得第一个参数,请查看$_[0]或执行my $foo = shift;。 (shift默认情况下@_运行。)

至于any,我相信这个问题是由于any不能够在运行时加载它的原型(子程序原型只能在编译时被调用。)您可能需要使用明确的parens和明确的子程序引用:

any(sub { $value eq $_ }, qw(fatal keep merge non-fatal replace)); 
+0

哇。我一直使用@_进行移位或列表赋值,并且$ _很常见,我完全忘记了它不会自动设置。 – gvkv 2010-01-19 16:00:21

+0

我刚刚尝试了解'任何'的解决方案,但没有运气。这里有一个更一般的问题,就是如何用eval或其他方法定义一个带有字符串的子程序。也许我会稍后再问。 – gvkv 2010-01-19 16:05:50

+0

我修好了。 List :: MoreUtils默认不会导出任何东西。 – gvkv 2010-01-19 16:15:33

不要在配置中编写代码。用代码创建一个库,并简单地配置要使用的子程序名称。这应该可以为您节省大量的翻译字符串的代码和管理过程。当有人调整配置并引入语法错误时,它还可以节省大量的时间追踪问题。

我在Mastering Perl的“配置”一章以及动态子程序章节中对此进行了广泛的讨论。

代码不属于配置。说,直到你相信它。

+0

你当然是对的,但我认为单线并不是什么大不了的事情(而且我违反了这个原则)。 – gvkv 2010-01-21 11:12:29

+0

另一个问题是,我不知道如何做到这一点,而不关闭严格的裁判。 – gvkv 2010-01-21 12:01:45