2010-12-30 2 views
1

Продолжая путь от моего previous question, я столкнулся с другой проблемой по дороге. Я понял, что не только в хешах есть хеши, в хешах также могут быть массивы. Поэтому пути было бы нечто вродеУдаление и добавление ссылок на хеш и массив в Perl

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?) и эффективно удалить и переименовать ключи хэша?

Спасибо.

+1

Мне кажется, что сложно построить что-то простое и непротиворечивое, если вы можете получить как 'two', так и' two [] 'на том же уровне. –

+0

Yup, 'two' и' two [] 'всегда будут на одном уровне и могут встречаться в случайном порядке, например (' two', 'two []', 'two' и т. Д. (Так я получил хэш как я уже переименовал 'two []' в 'two' и столкнулся с другим' two')) –

+0

Извините, я до сих пор не вижу, как строится эта структура. Не могли бы вы проследить его содержимое после обработки каждой строки, объяснив, почему там происходят изменения? –

ответ

1

Хорошо, это должно сосать, а не делать то, что вы хотите. Но я потратил последний час, пытаясь получить его несколько правильно, поэтому я буду проклят. Каждое «все []» представляет собой массив из двух элементов, каждый из которых hashref: один для элементов, которые появляются после голого «ничего», а второй для элементов, появляющихся после «ничего []». Вероятно, я должен был использовать закрытие вместо того, чтобы полагаться на эту дерьмовую переменную $ is_non_bracket. Еще утром я пойду еще раз, когда я буду меньше отставать и больше стыжусь писать это.

Я думаю, что он оптимизирован для хвостового оплета (часть & 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 

РЕДАКТ. У Regex было дополнительное пространство после кэметы, которое заставило его сломаться; Виноват.

EDIT2: Хорошо, это утро, отредактированное в версии, которая не настолько глупа. Нет необходимости в ref, так как мы всегда передаем hashref; # /, Чтобы остановить // от borking выделения.

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, которое быстро втягивается в подстановку, которая отнимает у нее [], если она есть: если это так, substitution возвращает 1, в противном случае s /// возвращает undef (или пустую строку, я забыл), поэтому мы используем логический - или для установки возвращаемого значения на 0; В любом случае мы устанавливаем для этого $ is_bracket.

После этого, если $ node -> {$ next} является скобкой arrayref или $ next: Если $ node -> {$ next} не был arrayref (так мы получили здесь, потому что у $ next были скобки, и это было в первый раз, когда это произошло), это либо undef, либо пустая строка, либо hashref; Мы удаляем все, что есть, и храним его в $ temp. Затем мы устанавливаем теперь пустой пул $ node -> {$ next} в arrayref и устанавливаем (push) $ temp в качестве своего первого элемента. Это означает, что, например, если «два» существовали ранее, а $ next было первоначально 'two []', затем 'two' теперь укажет на arrayref, и его старое значение будет сохранено в [0]. После того, как $ node -> {$ next} является arrayref (или если он уже был), мы деактивируем hashref в индексе, указанном $ is_backet - 0, если $ next не имеет скобок, и 1, если это было - @_. Если hashref не существует (либо потому, что он является undef, для обеих, или, возможно, для пустой строки, для 0), мы назначаем ему новый hashref с логическим или.

Если это не массив, это hashref, поэтому мы делаем то же самое, что и раньше, и смещаем полученное значение на @_.

Мы делаем все это без сдвига, потому что магия goto передает наш текущий @_ функции, которая нас заменит.

+0

Благодарим за это - пока я до сих пор не совсем понимаю, как это работает, я с нетерпением жду такой кодировки когда-нибудь. Чтобы заставить его работать, я манипулировал хешем после его создания, переименовав и удалив ключи (не самое элегантное решение, но оно работает). Спасибо :) –

+0

Не волнуйся, я сам этого не понимаю:) Какого черта, о чем я думаю, серьезно. Я наберу небольшое резюме того, что он должен делать за пару минут, и отредактируйте его. – Hugmeir

1

Я не уверен, по какой логике вы достигнете ожидаемого результата, но я могу пояснить следующее:

  • Вы можете проверить тип ссылки с помощью ref function.
  • В вашем текущем коде $ ref рассматривается как хэш-ссылка в каждом отдельном случае. Я могу сказать, потому что вы разыгрываете его как хэш, используя синтаксис $ ref -> {...} в каждом отдельном предложении ваших операторов if.
  • Если я правильно прочитал, $1 . $2 должен быть таким же, как $head. Я нахожу это более понятным, как только $head.
  • Строка unshift vivifies $ref->{$1 . $2} как ссылка на массив (в явный пустой массив). И следующая строка оживляет свой первый элемент как хеш-ссылку. Первая строка кажется мне лишней, вы получите те же результаты с единственной линией insert; поэтому я не уверен в намерениях.
+0

> Re: '$ 1. $ 2' такой же, как '$ head', когда' $ head' is '. * []', Это правильно, я экспериментировал с использованием только '$ 1', но это приводило к проблемам в' if' clause с линией 'insert (\% {$ ref -> {$ head}}, @tail); '. –

+0

> Re using 'ref', я использовал' ref (\% {$ ref -> {$ head}}) eq 'HASH'', но это также дало мне ошибки, если '$ ref -> {$ head}' был массив. –

Смежные вопросы