3

use strict在过去的几年里,我们已经在我们遗留的 Perl 代码库中采用了。我的任务是将它添加到其余模块中,同时确保它当然不会破坏任何东西。

现在这很容易use strict 'vars'use strict 'subs'因为这些是简单perl -c捕获的编译时错误。但是有没有一种系统的方法来检查由 触发的运行时错误use strict 'refs'

当然,我可以通过在所有可能的情况下(即完全覆盖)调用所有函数来引发运行时错误,但这很麻烦,尤其是。因为这段代码缺少单元测试。如果您有更好的想法,我将不胜感激。

4

1 回答 1

4

因为 Perl 是无类型的(变量可能包含引用或非引用),所以只能在运行时检查。考虑:

use strict;

# Usage: foo(HASHREF)
sub foo {
   print $_[0]{name}, "\n" if int(rand(2));
}

my $var = int(rand(2))
   ? { name => "Hello" }
   : "World";  # oopsie!

foo($var);

大约 25% 的时间会失败。无类型语言的静态分析无法捕捉到这种东西——它只能被你的测试套件捕捉到,所以你的首要任务应该是改进它。

也就是说,有一些方法可以改进可以帮助您的代码。例如,foo如果我们对传入的参数进行类型检查,我们可以证明 sub 本身是正确编写的:

sub foo {
   my $href = $_[0];
   ref($href) eq 'HASH' or die("Expected hashref!");
   print $href->{name}, "\n" if int(rand(2));
}

foo这将确保' 行为正确性的负担从' 调用者身上推foofoo' 调用者身上。现在,您的内部foo有一小段代码,您可以在其中确信每个变量的数据类型。逐步添加这样的断言可以让您不断扩大“信心区域”。

请注意,上面的小变化意味着我们的代码现在将失败 50% 的时间,而不是 25% 的时间。因为foo现在失败更加一致,这将帮助我们抓住错误的真正来源:

my $var = int(rand(2))
   ? { name => "Hello" }
   : "World";  # oopsie!

foo($var);

部分是因为我写了它,部分是因为它非常快,我建议你看看Type::Params来检查子参数。以下是如何使用它的示例:

use feature qw(state);
use Types::Standard qw( HashRef );
use Type::Params qw( compile );

sub foo {
   state $signature = compile( HashRef );
   my ($href) = $signature->(@_);
   print $href->{name}, "\n" if int(rand(2));
}

将其扩展为带有多个参数的子...

use feature qw(state);
use Types::Standard qw( -types );
use Type::Params qw( compile );

sub query_get_rows {
   state $signature = compile( InstanceOf['DBI::db'], Str, Optional[Int] );
   my ($dbh, $query, $limit) = $signature->(@_);

   # do stuff here
}
于 2014-04-17T09:05:00.830 回答