在Perl中删除和添加哈希和数组引用

时间:2010-12-30 23:23:02

标签: arrays perl hash reference perl-data-structures

继续我的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?)和有效删除和重命名哈希的键?

感谢。

2 个答案:

答案 0 :(得分:1)

我不确定你达到预期输出的逻辑,但我可以澄清以下内容:

  • 您可以使用ref function检查参考的类型。
  • 在您当前的代码中,$ ref在每种情况下都被视为哈希引用。我可以告诉你,因为你在if语句的每个子句中使用$ ref-&gt; {...}语法将其取消引用为哈希。
  • 如果我读错了,$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将我们当前的@_传递给将取代我们的函数。