#perl #hash
#perl #хэш
Вопрос:
Я имею дело с хэш-таблицей в perl.
У меня есть несколько строк с несколькими длинами и несколькими -
:
pre1-pre2-text1-text2
pre3-text3
pre4-pre5-pre6-text4
У меня есть a %hash
со следующими ключами:
pre1-pre2
pre3
pre4-pre5-pre6
Таким образом, ключи %hash
содержат только pre
часть строк.
Как я могу проверить, есть ли совпадение, скажем, между первой строкой pre1-pre2-text1-text2
и ключами %hash
?
Ответ №1:
Один из способов: сформировать шаблон, используя чередование ключей, и протестировать строки по нему
use warnings;
use strict;
use feature 'say';
my @strings = qw(pre-not pre1-pre2-text1-text2 pre3-text3 pre4-pre5-pre6-text4);
my %h = ( 'pre1-pre2' => 1, 'pre3' => 1, 'pre4-pre5-pre6' => 1 );
my $keys_re = join '|', map { quotemeta } keys %h;
foreach my $str (@strings) {
say $str if $str =~ /$keys_re/;
}
Это имеет квадратичную сложность, но чередование не будет проходить через все ключи, и это C (само регулярное выражение).
Возможное улучшение (или необходимость!) может быть, для подходящей сортировки ключей. Например, кратчайший первый
my $keys_re = join '|', map { quotemeta } sort { length $a <=> length $b } keys %h;
Это может помочь, если есть ключи с общими частями, но учтите, что это может быть нетривиальная корректировка, которая может повлиять на корректность — и которая может потребоваться; подумайте внимательно.
Чтобы также получить сам ключ, добавьте захватывающую скобку вокруг шаблона
foreach my $str (@strings) {
say "$str matched by key: $1" if $str =~ /($keys_re)/;
}
где $1
содержит совпадающее и захваченное чередование, которое является ключом.
Комментарии:
1. Спасибо. Условие if оценивается как true, так что это кажется правильным. Однако, как я могу теперь распечатать совпадение и получить доступ к значению, связанному с этим совпадением, в моем исходном%h?
2. Добро пожаловать @user1987607. Для этого вы хотите зафиксировать совпадение. Это хороший момент, он должен быть в сообщении — добавлено примечание.
Ответ №2:
Я добавил входные данные, предоставленные вами в небольшом коде perl, и я могу проверить, есть ли совпадение в ключах
#!/usr/bin/perl
use warnings;
my %langs = ( "pre1-pre2" => 'pre1-pre2',
"pre3" => 'pre3',
"pre4-pre5-pre6" => 'pre4-pre5-pre6');
@pats=("pre1-pre2-text1-text2", "pre3-text3", "pre4-pre5-pre6-text4");
for(keys %langs){
foreach $ss (@pats){
if (index($ss,$_) != -1){
print("Key contains:",$_, "|", $ss,"n");
}
else{
print("NOT FOUND:",$_, "|", $ss,"n");
}
}
}
ПРИМЕЧАНИЕ: Если я правильно понял ваше требование, это вам поможет.
Комментарии:
1. Здесь несколько интересных вариантов кодирования. Почему
use warnings
, но нетuse strict
? Почему объявляется%langs
сmy
помощью, но без других переменных? Зачем переключаться междуfor
иforeach
? Зачем передавать несколько аргументовprint()
вместо интерполяции переменных в одну строку? Зачем использоватьprint()
, нетsay()
?2. И если это «правильный» ответ, которым он вполне может быть, то хэши на самом деле ничего вам не дают. Если вы не можете извлечь ключ для поиска O (1), то в хэше мало смысла (по крайней мере, в этом случае), и вы можете также использовать массив.
3. @DaveCross. Прошло 8 лет, я работал в Perl, и поэтому я искал примеры и создал свой пример. Так что будут некоторые проблемы 🙂
4. @Raghuram: Это проблема с объединением кода из разных источников. В одном и том же примере вы можете получить совершенно разные стили, и это может легко запутать новичка. Поставьте себя на место новичка, читающего ваш код. Я бы подумал: «итак, вы используете
for
для перебора хэша иforeach
перебора массива» — и это просто неправильно.
Ответ №3:
Этот ответ предполагает, что pre
это не может произойти в середине строки (т. Е. У вас не будет строки, подобной pre1-pre2-text1-pre5
тому, где будет только ваш префикс pre1-pre2
). Если это предположение неверно, используйте /^((?:pred )(?:-pred )*)/
вместо /^(.*pred )/
(я предпочитаю последнее, потому что оно более читабельно, но первое более точное).
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my %pre = map { $_ => 1 } qw(pre1-pre2 pre3 pre4-pre5-pre6);
while (<DATA>) {
my ($prefix) = /^(.*pred )/;
if ($prefix amp;amp; exists $pre{$prefix}) {
say "Prefix exists: $prefix";
} else {
say "Prefix doesn't exist: $prefix";
}
}
__DATA__
pre1-pre2-text1-text2
pre3-text3
pre4-pre5-pre6-text4
pre7-pre8-text5
Если у вас может быть строка pre1-pre2-text1
, где префикс должен быть просто pre1
, то это решение не будет работать. В этом случае у вас не будет другого выбора, кроме как перебрать все ключи хэша и проверить, соответствуют ли они началу строки:
while (<DATA>) {
for my $prefix (keys %pre) {
if (/^Q$prefix/) {
say "Found prefix: $prefix";
last;
}
}
}
Однако это гораздо менее эффективно, поскольку вам нужно перебирать все хэш-ключи для каждой строки.
Относительно Q
: это гарантирует, что это решение работает, даже если ваши префиксы содержат специальные символы регулярных выражений (например
, или .
). Если ваши префиксы всегда одинаковы pre1-pre2
, тогда вы можете опустить Q
.
Если у вас возникли проблемы с пониманием my %pre = map { $_ => 1 } qw(pre1-pre2 pre3 pre4-pre5-pre6);
: это краткая версия
my %prev = (
'pre1-pre2' => 1,
'pre3' => 1,
'pre4-pre5-pre6' => 1
);