Ладно, по всем правилам, это должно быть отстойно, а не делать то, что вы хотите - Но я провел последний час, пытаясь сделать это несколько правильно, поэтому я буду проклят. Каждое «что-нибудь []» представляет собой массив из двух элементов, каждый из которых имеет хэш-ссылку: один для элементов, которые появляются после пустого «чего-либо», а второй для элементов, появляющихся после «чего-нибудь []». Вероятно, мне следовало бы использовать замыкание вместо того, чтобы полагаться на эту дрянную переменную $ 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
РЕДАКТИРОВАТЬ: у Regex был дополнительный пробел после кавычки, из-за которой он сломался; Мой плохой.
EDIT2: Хорошо, это утро, отредактированное в версии, которая не так глупа. Нет необходимости в ссылке, так как мы всегда передаем хэш-ссылку; Символы # / предназначены для того, чтобы // не могли выделить подсветку.
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 (или забываю пустую строку), поэтому мы используем логическое or или для установки возвращаемого значения в 0; В любом случае, мы устанавливаем $ is_bracket в это.
Впоследствии, если $ node -> {$ next} - это arrayref, или $ next содержит скобки:
Если $ node -> {$ next} не был arrayref (поэтому мы попали сюда, потому что $ next имел квадратные скобки, и это было в первый раз), это либо undef, пустая строка, либо hashref; Мы удаляем что бы то ни было и сохраняем его в $ temp. Затем мы устанавливаем пустое $ node -> {$ next} равным arrayref и устанавливаем (push) $ temp в качестве первого элемента - это означает, что, например, если 'two' существовало ранее, а $ next было изначально 'two []', затем 'two' теперь будет указывать на arrayref, а его старое значение будет сохранено в [0].
Как только $ node -> {$ next} является ссылкой на массив (или, если он уже был), мы снимаем хэш-ссылку в индексе, указанном в $ is_backet - 0, если у $ next нет скобок, и 1, если у него есть - to @ _. Если hashref не существует (или потому что он undef для обоих, или, возможно, пустая строка, для 0), мы назначаем ему совершенно новый hashref с логическим или.
Если это был не arrayref, это hashref, поэтому мы делаем то же самое, что и раньше, и возвращаем полученное значение в @ _.
Мы делаем все это без смещения, потому что волшебное goto передает наш текущий @_ функции, которая заменит нас.