Как отключить автовивитацию в Perl?
Предположим, что у вас есть ОГРОМНОЕ приложение "развито";) большой командой.
Вот упрощенная модель потенциального бедствия, которое может произойти, когда кто-то слишком глубоко проверяет структуру данных.
Если невозможно полностью отключить автообвиление или в объеме, как обойти это?
Большое спасибо:)!!!!
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:: StrictHash, Tie:: Hash и perltie.
Вот упрощенная версия моего решения:
#!/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 хорош для больших приложений тоже, вам просто нужно знать больше;).
Спасибо всем!
Надеюсь, это тоже поможет другим.
Ответы
Ответ 1
Возможно, вы захотите использовать объект вместо хэша (см. Moose) или используйте строгий связанный хэш. Или вы можете включить предупреждения в ошибки, если вы действительно хотите:
use warnings NONFATAL => 'all', FATAL => 'uninitialized';
Ответ 2
Относительно новый модуль autovivification
, который позволяет это сделать:
no autovivification;
Довольно просто.
Ответ 3
Вы можете заблокировать хэш, используя одну из функций 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
Ответ 4
Я одобрил @zoul, но вы должны сделать это еще на один шаг.
Записать тесты
У вас должен быть ваш код с тестами, и вы должны запустить некоторые из этих тестов с помощью
use warnings FATAL => 'uninitialized';
объявленного в самом тестовом случае. Это единственный способ решить проблему, с которой вы сталкиваетесь с разработчиками, не проверяя заранее. Убедитесь, что их код проверен.
И сделайте еще один шаг вперед и упростите выполнение тестов в Devel::Cover, чтобы получить отчет о покрытии.
cover -delete
PERL5OPT='-MDevel::Cover' prove -l
cover -report Html_basic
И затем проверьте, что строки кода и операторы выполняются с помощью тестов, в противном случае, если эти предупреждения будут смертельными, это просто запустит код в неожиданное время позже.
Ответ 5
Другой вариант - использовать Data:: Diver для доступа к вашим структурам данных.
if( 1 == Dive($some_ref, qw/ deep structures are not autovivified now / ) {
Do_Stuff();
}