如何在Perl中禁用自动修复?

时间:2009-04-26 09:00:27

标签: perl autovivification

假设您有一个巨大的应用程序“开发”;)由一个大团队。 以下是当某人检查数据结构太深时可能发生的潜在灾难的简化模型。 如果无法完全或在范围内禁用自动验证,如何解决此问题? 非常感谢:) !!!!

use strict; use warnings;use Data::Dumper;

my $some_ref = {akey=>{deeper=>1}};
print Dumper($some_ref );
if($some_ref->{deep}{doot} == 1){
    print 'too deep '.$/;
}

if($some_ref->{deep}){
    print 'Already in a deep doot'.$/;
}

print Dumper($some_ref );

这输出以下内容:

$VAR1 = {
          'akey' => {
                      'deeper' => 1
                    }
        };
Use of uninitialized value in numeric eq (==) at autovivify_test.pl line 5.
Already in a deep doot
$VAR1 = {
          'deep' => {},
          'akey' => {
                      'deeper' => 1
                    }
        };

是的,我知道有警告,但......可能为时已晚。

嘿伙计们,我的hashref引用一个绑定的HASH可能会有所帮助。

如果我实现了一个好的FETCH方法来检查结构中更深层次的检查,我可以轻松解决我的问题吗?


我查看了Tie::StrictHashTie::Hashperltie。 这是我的解决方案的简化版本:

#!/usr/bin/env perl;
#test_tie.pl

package StrictHash;
use strict; use warnings;
use Tie::Hash;
our @ISA = qw(Tie::StdHash);
use Carp;

sub TIEHASH {
    my $class = shift;
    my $hash = bless {@_}, $class;
    return $hash;
}
##========================================================================
## FETCH fails if applied to a member that doesn't exist.
##========================================================================
sub FETCH {
    my ($hash, $key) = @_;
    Carp::confess "key '$key' does not exist" unless exists $hash->{$key};
    return $hash->{$key};
}
##========================================================================
package main;
use strict;use warnings;use Data::Dumper;
#Imagine StrictHash is in ./StrictHash.pm
#use StrictHash;
my %hash;
tie %hash, 'StrictHash', akey => {deeper=>1} ;  

my $some_ref =\%hash;
print Dumper($some_ref );
if($some_ref->{deep}{doot} == 1){
    print 'too deep '.$/;
}

我取得的成就是只触摸应用中的一个位置。 现在所有像if($ some_ref-> {deep} {doot})这样的地方都会导致堆栈跟踪死亡。 所以我很容易找到它们并纠正它们。 这种新的着作是不可能的。 Perl也适用于大型应用程序,你只需要知道更多;)。

谢谢大家! 我希望这也有助于其他人。

5 个答案:

答案 0 :(得分:21)

相对较新的是autovivification模块,可以让你这样做:

no autovivification;

非常简单。

答案 1 :(得分:15)

您可能希望使用对象而不是哈希(请参阅Moose)或使用strict tied hash。或者你可以将警告变成错误,如果你真的想:

use warnings NONFATAL => 'all', FATAL => 'uninitialized';

答案 2 :(得分:9)

您可以使用Hash::Util(核心模块)中的一个函数锁定哈希值。

use Hash::Util qw( lock_keys unlock_keys );

my $some_ref = { akey => { deeper => 1 } };
lock_keys %$some_ref;

print "too deep" if $some_ref->{deep}{shit} == 1;

现在最后一个语句将引发异常:

Attempt to access disallowed key 'deep' in a restricted hash

当然,缺点是在检查散列中的密钥时必须非常小心以避免异常,即在访问密钥之前使用lof“if exists ...”来检查密钥

如果您需要稍后再次向哈希添加密钥,则可以将其解锁:

unlock_keys %$some_ref;
$some_ref->{foo} = 'bar'; # no exception

答案 3 :(得分:3)

我赞成@zoul,但你应该更进一步。

撰写测试

您的代码应该包含测试,您应该使用

运行其中一些测试
use warnings FATAL => 'uninitialized';

在测试用例本身中声明。它是解决您对开发人员的问题的唯一方法,而不是事先正确地检查事物。确保他们的代码经过测试。

再进一步,让您可以轻松地在Devel::Cover下运行测试,以获得报道。

cover -delete
PERL5OPT='-MDevel::Cover' prove -l 
cover -report Html_basic 

然后检查代码行和测试正在执行的语句,否则使这些警告致命只会使代码在以后的意外时间死亡。

答案 4 :(得分:2)

另一个选择是使用Data::Diver来访问您的数据结构。

if( 1 == Dive($some_ref, qw/ deep structures are not autovivified now / )) {
    Do_Stuff();
}