如何在Perl中禁用自动创建嵌套数据结构的功能(autovivification)?

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
                    }
        };

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

需要说明的是,我的哈希引用了一个绑定的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也适用于大型应用程序,你只需要了解更多 ;). 谢谢大家! 希望这对其他人也有所帮助。

我该如何在Perl中禁用自动创建数据结构的功能? - Беров
5个回答

22

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

no autovivification;

非常简单明了。


2
当前不依赖版本的链接: http://search.cpan.org/~vpit/autovivification/ - Pablo Marin-Garcia
我在 Perl Monks 上提到了这个问题,一位资深的“和尚”建议谨慎使用 'autovivification',因为它是一个相当长的 XS 代码段(即 1788 行相对晦涩的 C 代码)。该 pragma 显然有效,但请注意,它可能导致 Perl 解释器出现一些微妙和奇怪的 bug - 特别是因为它是新的。如果你发现了一个 bug,请在 'Issues' 页面 上报告它。 - Medlock Perlman
1
好的,当我六年前发布这个答案时,这个模块还很新。现在看来,它似乎得到了积极维护,并配备了全面的测试套件。但当然,将信任放在哪里是个人决定。 - oeuftete

16

你可能想使用一个对象来代替哈希表(查看 Moose)或使用严格绑定的哈希表。如果真的想要,也可以将警告转换为错误:

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

也许像肯特所说的那样,使用 warnings NONFATAL => 'all', FATAL => 'uninitialized'; 是最可接受的方法。我可能已经在开发中设置了这个。我会再多等一会儿,看看是否有人会提出更聪明的建议 : )。谢谢。 - Беров
我刚意识到,由于我的引用指向一个绑定的哈希表,所以我只需要在我的FETCH方法中编写一些检查。非常感谢。 - Беров
唯一不需要重构应用程序的方法是将哈希绑定在每个级别上。这样做会有相当大的性能损失,但您已经在支付这种惩罚了。 - Michael Carman

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 ...”来检查键是否存在。
如果您需要稍后再次向哈希表中添加键,则可以解锁它:
unlock_keys %$some_ref;
$some_ref->{foo} = 'bar'; # no exception

好的,但是这是一个很大的应用程序。想象一下哈希是一个绑定的会话。谢谢。 - Беров

3

我赞同了@zoul的说法,但你需要更进一步。

编写测试

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

use warnings FATAL => 'uninitialized';

在测试用例中声明。这是解决开发人员没有充分检查事项的唯一方法。确保他们的代码经过测试。

并且可以进一步采取措施,使测试在Devel::Cover下运行,以获取覆盖率报告。

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

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


1
你不需要使用 FATAL => 'uninitialized'。Test::Warn 模块可以让你测试你的测试用例是否收到了正确的警告(或无警告)。 - Dave Sherohman
“写测试”这个说起来很容易 :). 我已经开始做了,但是你知道任务还在等待中。与此同时,我需要做一些更简单的事情。感谢您的支持。我正在考虑在开发环境中使用 FATAL => 'uninitialized'。 - Беров
"use warnings"是词法作用域的,因此在测试中打开它并不会使被测试的代码启用它。 - ysth

2
另一个选择是使用Data::Diver来访问您的数据结构。
if( 1 == Dive($some_ref, qw/ deep structures are not autovivified now / )) {
    Do_Stuff();
}

谢谢,我只是想要一个快速解决我的问题并停止进一步的错误行为的方法。我将在我的问题中添加一个简化的解决方案模型。 - Беров

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