继续我的previous question,我遇到了另一个问题。我意识到不仅哈希内部会有哈希,哈希内部也可能有数组。所以路径就像
one/two/three
one/two[]/three
one/two/four
one/two[]/four
即。哈希应该包含数组将始终以[]
作为后缀。根据我正在使用的脚本(我上一个问题的答案的略微修改版本),上述路径将导致:
one => {
two => {
three => "",
four => "",
}
two[] => [
{
three => "",
four => "",
}
]
}
我正在使用的脚本是:
# !/usr/bin/perl
use Data::Dumper;
sub insert {
my ($ref, $head, @tail) = @_;
if ( @tail ) {
if( $head !~ /^(.*)(\[\])$/ ) {
insert( \%{$ref->{$head}}, @tail );
} else {
my %newhash = ();
unshift(@{$ref->{$1 . $2}}, %newhash);
insert( \%{$ref->{$1 . $2}[0]}, @tail );
}
} else {
$ref->{$head} = '';
}
}
my %hash;
chomp and insert \%hash, split( '/', $_ ) while <>;
print Dumper %hash;
我想要做的是,一旦找到two[]
,我就要删除two
并将其添加到two[]
数组中(如果two
})然后将键two[]
重命名为two
。
所以最终结果看起来像是:
one => {
two => [
{
three => "",
four => "",
},
{
three => "",
four => "",
}
]
}
所以我尝试在if else
中添加支票,用于检查带有或不带[]
后缀的密钥,但是我得到了一些范围或错误,例如[$variable] is not a valid HASH reference
等。如何检查变量的类型(例如$ref->{$head} is array?
)和有效删除和重命名哈希的键?
感谢。
答案 0 :(得分:1)
我不确定你达到预期输出的逻辑,但我可以澄清以下内容:
ref
function检查参考的类型。$1 . $2
应与$head
相同。我发现它只是$head
。unshift
行将$ref->{$1 . $2}
视为数组引用(显式为空数组)。并且下一行将其第一个元素作为哈希引用。第一行对我来说似乎是多余的,你可以用唯一的insert
行获得相同的结果;所以我不确定意图。答案 1 :(得分:1)
好的,通过所有的权利,这应该很糟糕,而不是做你想要的 - 但我花了最后一小时试图让它有点正确,所以我会被诅咒。每个'anything []'是一个由两个元素组成的数组,每个元素都是一个hashref:一个用于出现在“任何东西”之后的元素,另一个用于出现在“之后[]”之后的元素。我可能应该使用一个闭包而不是依赖那个糟糕的$ is_non_bracket变量 - 我会在早上再看一看,因为我不那么迟钝,而且更羞于写这个。
我认为它是尾调用优化(goto&amp; SUB部分)。它也(小)使用named captures。
use strict;
use warnings;
use 5.010;
use Data::Dumper;
sub construct {
my $node = shift;
return unless @_;
my $next = shift;
my $is_non_bracket = 1;
$next .= '[]' and $is_non_bracket-- if exists $node->{ $next . '[]' };
if ( $next =~ / (?<node>[^\[\]]+) \Q[]/x ) {
if ( exists $node->{ $+{node} } or not defined( $node->{$next} ) ) {
push @{ $node->{$next} }, (delete $node->{ $+{node} } // {}); #/
}
unshift @_, $node->{$next}->[$is_non_bracket] ||= {};
}
else {
$node->{$next} ||= @_ ? {} : $node->{$next};
unshift @_, $node->{$next} //= @_ ? {} : ''; #/
}
goto &construct;
}
my %hash;
while (<DATA>) {
chomp;
construct( \%hash, split m!/! );
}
say Dumper \%hash;
__DATA__
one/two/three
one/two[]/three
one/two[]/three/four
one/two[]/three/four/five[]
one/two[]/three/four/whatever
one/two/ELEVAN
one/three/sixteen
one/three[]/whygodwhy
one/three/mrtest/mruho
one/three/mrtest/mruho[]/GAHAHAH
编辑:正则表达式在引用它之后有一个额外的空间使其崩溃;我的坏。
EDIT2:好的,现在是早上,编辑的版本不是那么愚蠢。不需要ref,因为我们总是传递hashref; #/是否可以阻止这些突出显示。
EDIT3:刚注意到你不希望那些[]出现在数据结构中,所以这里的版本没有显示出来:
sub construct {
my $node = shift;
return unless @_;
my $is_bracket = (my $next = shift) =~ s/\Q[]// || 0;
if (ref $node->{$next} eq 'ARRAY' or $is_bracket) {
if ( ref $node->{ $next } ne 'ARRAY' ) {
my $temp = delete $node->{ $next } || {};
push @{ $node->{$next} = [] }, $temp;
}
unshift @_, $node->{$next}->[$is_bracket] ||= {};
}
else {
$node->{$next} ||= @_ ? {} : $node->{$next};
unshift @_, $node->{$next} //= @_ ? {} : ''; #/
}
goto &construct;
}
EDITNaN: 以下是它的作用要点: 如果有足够的参数,我们会再次移动并将值放在$ next中,这会立即被拉入替换中,如果它有任何替换,它将带走[],如果有,则替换返回1,否则,s ///返回undef(或空字符串,我忘了),所以我们使用逻辑 - 或者将返回值设置为0;无论哪种方式,我们都将$ is_bracket设置为此。
之后,如果$ node-&gt; {$ next}是一个arrayref或$ next有括号: 如果$ node-&gt; {$ next}不是一个arrayref(所以我们在这里因为$ next有括号,这是第一次发生这种情况),它'要么是undef,要么是空字符串,要么是hashref;我们删除它是什么,并将其存储在$ temp中。然后我们将现在为空的$ node-&gt; {$ next}设置为arrayref,并设置(push)$ temp作为其第一个元素 - 例如,如果先前存在'two',则$ next是最初'two []',然后'two'现在指向一个arrayref,它的旧值将存储在[0]中。 一旦$ node-&gt; {$ next}是一个arrayref(或者它已经是),我们将$ is_backet指向的索引中的hashref取消 - 如果$ next没有括号,则取消0,如果是,则取消1 @_。如果hashref不存在(因为它是undef,对于两者,或者可能是空字符串,对于0),我们为它分配一个带有逻辑的全新hashref。或者
如果它不是一个arrayref,那么它是一个hashref,所以我们做了和以前一样的事情,并将结果值取消移动到@_。
我们做了所有这些不移位,因为神奇的goto将我们当前的@_传递给将取代我们的函数。