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[]twotwo[]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}は、(明示的な空の配列への)配列参照として生き生きとします。次の行は、最初の要素をハッシュ参照として有効にします。最初の行は不要に思えます。1 行だけでも同じ結果が得られますinsert。なので意図がわかりません。
于 2010-12-30T23:46:11.933 に答える
1

さて、当然のことながら、これはひどいものであり、あなたが望むことをしないはずです-しかし、私は最後の1時間、それをある程度正しくするために費やしたので、私はのろわれます. 各 'anything[]' は 2 つの要素の配列であり、それぞれが hashref です。くだらない $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

編集:正規表現には、引用メタの後に余分なスペースがあり、それが壊れました。私の悪い。

EDIT2: わかりました、それは朝です、それほど愚かではないバージョンで編集されました. 常に hashref を渡すため、ref は必要ありません。#/ は、// が強調表示を中断するのを防ぐためにあります。

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} が arrayref であるか、$next に角かっこがある場合:これが発生したとき)、それは undef、空の文字列、または hashref のいずれかです。それが何であれ削除し、$temp に保存します。次に、空になった $node->{$next} を arrayref に設定し、$temp をその最初の要素として設定 (プッシュ) します。 'two[]' の場合、'two' は arrayref を指し、その古い値は [0] に格納されます。$node->{$next} が arrayref になったら (またはすでにあった場合)、$is_backet が指すインデックスの hashref のシフトを解除します - $next に括弧がない場合は 0、ある場合は 1 - @ _. hashref が存在しない場合 (それが undef であるため、

配列参照でない場合はハッシュ参照であるため、前と同じことを行い、結果の値を @_ にシフト解除します。

魔法の goto が現在の @_ を私たちを置き換える関数に渡すため、このすべてのシフト解除を行います。

于 2010-12-31T04:24:35.167 に答える