我正在使用动态多级哈希,我从中读取数据但也会写入数据。
我常见的陷阱是访问不存在的密钥(拼写错误,数据库修订版等)。我得到undef
s传播到其他部分并导致问题。每当我尝试读取不存在的密钥时,我想die
,但仍然可以添加新密钥。
所以想要的行为是:
my %hash;
$hash{A} = 5; # ok
print $hash{A}, "\n"; # ok
print $hash{X}, "\n"; # should die
$hash{B}{C}{D} = 10; # ok
print $hash{B}{C}{X}, "\n"; # should die
我之前发布过similar question并得到了很好的答案。我特别喜欢接受的,它允许使用普通的哈希语法。唯一的问题是我不确定如何轻松地将其概括为深哈希,如上例所示。
P.S。 我发现这个功能非常有用,我想知道我是否遗漏了一些东西,因为它似乎不太受欢迎。也许从同一个哈希读/写是不常见的?
答案 0 :(得分:3)
对我来说已经很晚了所以我会很简短,但是你可以使用tie
功能做到这一点 - 让你的哈希由下面的对象表示,并实现与哈希交互所需的功能。 / p>
查看perldoc -f tie;还有many classes on CPAN要查看,包括Tie::Hash本身,这是你可以构建的绑定哈希的一个很好的基类,覆盖一些方法来添加你的错误检查。
答案 1 :(得分:3)
如果你想围绕一个哈希包装检查,创建一个子程序来做它并将它用作你的界面:
use 5.010;
use Carp qw(croak);
sub read_from_hash {
my( $hash, @keys ) = @_;
return check_hash( $hash, @keys ) // croak ...;
}
但是现在你开始看起来像一个班级。当您需要专门的行为时,开始编写面向对象的类。做你需要做的事。我认为那是你失踪的部分。
坚持哈希界面的问题是人们希望哈希语法充当普通哈希值。当你改变这种行为时,其他人将很难弄清楚发生了什么以及为什么。
答案 2 :(得分:2)
打开warnings
pragma后,您会在要死的两行发出Use of uninitialized value in print at...
警告。
因此,如果你让warnings
致命,那么他们就会死:
use warnings FATAL => 'all';
的更新强>
根据您所做的评论,我认为您的常见案例问题是:
my $x = $hash{B}{C}{X};
在您稍后使用$x
之前,不会发出警告/错误。
要解决这个问题,你可以这样做:
my $x = $hash{B}{C}{X} // 'some default value';
my $z = $hash{B}{C}{Z} // die "Invalid hash value";
不幸的是,上述意味着很多额外打字:(
这至少是一个捷径:
use 5.012;
use warnings FATAL => 'all';
use Carp 'croak';
# Value Or Croak!
sub voc { $_[0] // croak "Invalid hash" }
然后下面会呱呱叫!
my $x = voc $hash{B}{C}{X};
希望这个以及致命的警告对你有帮助。
/ I3az /
答案 3 :(得分:2)
如果您不知道哈希可能具有哪些密钥,请使用其中一个绑定哈希建议或仅打开警告。请注意,绑定非常慢,比常规哈希慢9倍,比对象慢3倍。
如果你有一个固定集的可能键,你想要的是restricted hash。受限制的哈希只允许您访问一组给定的密钥,如果您尝试访问其他任何密钥,则会抛出错误。它也可以递归。这比搭售要快得多。
否则,我建议使用方法而不是直接散列访问将数据转换为对象。这比散列或受限散列慢,但比绑定散列更快。 CPAN上有许多模块可以从Class::Accessor开始为您生成方法。
如果您的数据没有修复,您可以编写简单的get()和set()方法,如下所示:
package Safe::Hash;
use strict;
use warnings;
use Carp;
sub new {
my $class = shift;
my $self = shift || {};
return bless $self, $class;
}
sub get {
my($self, $key) = @_;
croak "$key has no value" unless exists $self->{$key};
return $self->{$key};
}
sub set {
my($self, $key, $value) = @_;
$self->{$key} = $value;
return;
}
您可以通过在对象中存储对象来获得递归行为。
my $inner = Safe::Hash->new({ foo => 42 });
my $outer = Safe::Hash->new({ bar => 23 });
$outer->set( inner => $inner );
print $outer->get("inner")->get("foo");
最后,由于您提到了数据库修订,如果您正在从数据库中读取数据,那么您将需要查看对象关系映射器(ORM)以为您生成类和对象以及SQL语句。 DBIx::Class和Rose::DB::Object是两个很好的例子。
答案 4 :(得分:0)
tie
答案, 重新:您的评论 - hints on how to get the recursive effect
。
我不适合胆小的人,但下面是单向的一个基本示例,您可以使用Tie::Hash
执行您所追求的目标:
<强> HashX.pm 强>
package HashX;
use 5.012;
use warnings FATAL => 'all';
use Carp 'croak';
use Tie::Hash;
use base 'Tie::StdHash';
sub import {
no strict 'refs';
*{caller . '::hash'} = sub {
tie my %h, 'HashX', @_;
\%h;
}
}
sub TIEHASH {
my $class = shift;
croak "Please define a structure!" unless @_;
bless { @_ }, $class;
}
sub STORE {
my ($self, $key, $value) = @_;
croak "Invalid hash key used to store a value" unless exists $self->{$key};
$self->{$key} = $value;
}
sub FETCH {
my ($self, $key) = @_;
exists $self->{$key}
? $self->{$key}
: croak "Invalid hash key used to fetch a value";
}
1;
上面的模块就像一个严格的哈希。您必须预先声明哈希结构,然后任何FETCH或STORE将croak
,除非哈希键确实存在。
该模块有一个简单的hash
函数,该函数被导入到调用程序中,用于为所有工作构建必要的tie
。
use 5.012;
use warnings;
use HashX;
# all my hashref are ties by using hash()
my $hash = hash(
a => hash(
b => hash(
c => undef,
),
),
);
$hash->{a}{b}{c} = 1; # ok
$hash->{a}{b}{c} = 2; # also ok!
$hash->{a}{b}{d} = 3; # throws error
my $x = $hash->{a}{b}{x}; # ditto
请记住这是一个快速的&amp;肮脏的例子,并且未经测试超出上述范围。我希望它会让你了解如何使用Tie::Hash
完成它,甚至是否值得尝试:)
答案 5 :(得分:0)
使用Data::Diver中的DiveDie:
use Data::Diver qw(DiveDie);
my $href = { a => { g => 4}, b => 2 };
print DiveDie($href, qw(a g)), "\n"; # prints "4"
print DiveDie($href, qw(c)), "\n"; # dies