1

继续我之前的问题,我遇到了另一个问题。我意识到不仅哈希中应该有哈希,哈希中也可以有数组。所以路径会是这样的

one/two/three
one/two[]/three
one/two/four
one/two[]/four 

即散列应该包含该数组将始终以 a[]作为其后缀。根据我正在使用的脚本(我之前问题的答案的略微修改版本),上述路径将导致:

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?)并有效地删除和重命名散列的键?

谢谢。

4

2 回答 2

1

我不确定您通过什么逻辑达到预期输出,但我可以澄清以下内容:

  • 您可以使用函数检查引用的类型。ref
  • 在您当前的代码中, $ref 在每种情况下都被视为哈希引用。我可以说出来,因为您在 if 语句的每个子句中使用 $ref->{...} 语法将其取消引用为哈希。
  • 如果我没看错的话,$1 . $2应该和$head. 我觉得它更清楚$head
  • 您的unshift$ref->{$1 . $2}作为一个数组引用(指向一个明确的空数组)而活跃起来。下一行将其第一个元素激活为哈希引用。第一行对我来说似乎是多余的,你会得到与唯一insert行相同的结果;所以我不确定意图。
于 2010-12-30T23:46:11.933 回答
1

好吧,好吧,这应该很糟糕,不要做你想做的事 - 但我花了最后一个小时试图让它变得有点正确,所以我会被诅咒的。每个 'anything[]' 是一个包含两个元素的数组,每个元素都是一个 hashref:一个用于出现在裸 'anything' 之后的元素,第二个用于出现在 'anything[]' 之后的元素。我可能应该使用闭包而不是依赖那个糟糕的 $is_non_bracket 变量——我会在早上再看一遍,因为我不那么迟钝,更羞于写这篇文章。

我认为它是尾调用优化的(goto &SUB 部分)。它还(少量)使用命名捕获

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

编辑:正则表达式在导致它崩溃的quotemeta 之后有一个额外的空间;我的错。

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->{$next} 是一个数组引用或 $next 有括号:如果 $node->{$next} 不是一个数组引用(所以我们来到这里是因为 $next 有括号,它是第一个发生这种情况的时间),它要么是 undef,空字符串,要么是 hashref;我们删除它,并将其存储在 $temp 中。然后我们将现在为空的 $node->{$next} 设置为一个数组引用,并将 set(push) $temp 作为它的第一个元素——这意味着,例如,如果“两个”之前存在,并且 $next 最初是'two[]',然后'two'现在将指向一个arrayref,它的旧值将存储在[0]中。一旦 $node->{$next} 是一个 arrayref(或者如果它已经是),我们将 $is_backet 指向的索引中的 hashref 取消移位 - 如果 $next 没有括号,则为 0,如果有括号,则为 1 - 到 @ _。如果 hashref 不存在(或者因为它是 undef,

如果它不是一个数组引用,它就是一个哈希引用,所以我们做和以前一样的事情,并将结果值取消移动到@_。

我们做所有这些不移位是因为魔法 goto 将我们当前的 @_ 传递给将替换我们的函数。

于 2010-12-31T04:24:35.167 回答