8

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

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 也适用于大型应用程序,您只需要了解更多;)。

谢谢你们!我希望这对其他人也有帮助。

4

5 回答 5

22

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

no autovivification;

很简单。

于 2009-07-10T13:24:44.113 回答
16

您可能想要使用对象而不是哈希(请参阅Moose)或使用严格绑定的哈希。或者,如果您真的想要,您可以将警告变成错误:

use warnings NONFATAL => 'all', FATAL => 'uninitialized';
于 2009-04-26T09:29:42.110 回答
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

当然,不利的一面是,在检查散列中的键以避免异常时,您必须非常小心,即if exists ...在访问它们之前使用 " " 的 lof 来检查键。

如果您稍后需要再次将密钥添加到哈希中,您可以解锁它:

unlock_keys %$some_ref;
$some_ref->{foo} = 'bar'; # no exception
于 2009-04-26T10:22:02.127 回答
3

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

编写测试

你应该用测试覆盖你的代码,你应该运行其中一些测试

use warnings FATAL => 'uninitialized';

在测试用例本身中声明。这是解决您对开发人员未正确检查事物的担忧的唯一方法。确保他们的代码经过测试。

更进一步,让您可以轻松地在Devel::Cover下运行测试以获得覆盖率报告。

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

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

于 2009-04-26T10:42:26.070 回答
2

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

if( 1 == Dive($some_ref, qw/ deep structures are not autovivified now / )) {
    Do_Stuff();
}
于 2009-04-27T15:11:01.237 回答