Самый простой способ сопоставить массив строк для поиска в Perl? - PullRequest
7 голосов
/ 11 июня 2010

Что я хочу сделать, это проверить массив строк по моей строке поиска и получить соответствующий ключ, чтобы я мог сохранить его.Есть ли волшебный способ сделать это с Perl, или я обречен на использование цикла?Если да, то какой самый эффективный способ сделать это?

Я относительно новичок в Perl (я написал только 2 других сценария), поэтому я пока не знаю много магии,просто Perl - это магия = D

Reference Array: (1 = 'Canon', 2 = 'HP', 3 = 'Sony')
Search String: Sony's Cyber-shot DSC-S600
End Result: 3

Ответы [ 4 ]

11 голосов
/ 11 июня 2010

ОБНОВЛЕНИЕ:

На основании результатов обсуждения в этот вопрос , в зависимости от ваших намерений / критериев того, что составляет "не использовать цикл",Решение на основе map ниже (см. « Option # 1 ) может быть наиболее кратким решением, при условии, что вы не считаете map циклом (краткая версия ответов: это циклчто касается реализации / производительности, это не цикл с теоретической точки зрения языка).


Предполагая, что вам все равно, получите ли вы «3» или «Sony» в качестве ответа, вы можете сделать это без цикла в простом случае, построив регулярное выражение с "или" логикой (|) из массива, например:

my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
my $combined_search = join("|",@strings); 
my @which_found = ($search_in =~ /($combined_search)/); 
print "$which_found[0]\n";

Результат из моеготестовый прогон: Sony

Регулярное выражение (после интерполяции переменной Perl $combined_search) примет форму /(Canon|HP|Sony)/, что вам нужно.

Это НЕ будет работатькак есть, если любая из строк содержит специальные символы регулярного выражения (например, | или )) - в этом случае вам необходимо их избежать

ПРИМЕЧАНИЕ : лично я считаю это несколько обманом, потому что для реализации join() сам Perl должен выполнить цикл где-то внутриинтерпретатор.Таким образом, этот ответ может не удовлетворить ваше желание оставаться без цикла, в зависимости от того, хотите ли вы избежать цикла из соображений производительности, чтобы иметь более чистый или более короткий код.


PS Чтобы получить «3»вместо «Sony» вам придется использовать цикл - либо очевидным образом, выполнив 1 совпадение в цикле под всем этим;или используя библиотеку, которая избавляет вас от написания цикла самостоятельно, но под вызовом будет цикл.

Я предоставлю 3 альтернативных решения.

# 1 опция: - мое любимое.Использует «map», который я лично до сих пор считаю циклом:

my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
my $combined_search = join("|",@strings); 
my @which_found = ($search_in =~ /($combined_search)/); 
print "$which_found[0]\n";
die "Not found" unless @which_found;
my $strings_index = 0;
my %strings_indexes = map {$_ => $strings_index++} @strings;
my $index = 1 + $strings_indexes{ $which_found[0] };
# Need to add 1 since arrays in Perl are zero-index-started and you want "3"

# 2 option : Использует цикл, скрытый за красивым методом библиотеки CPAN:

use List::MoreUtils qw(firstidx);
my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
my $combined_search = join("|",@strings); 
my @which_found = ($search_in =~ /($combined_search)/); 
die "Not Found!"; unless @which_found;
print "$which_found[0]\n";
my $index_of_found = 1 + firstidx { $_ eq $which_found[0] } @strings; 
# Need to add 1 since arrays in Perl are zero-index-started and you want "3"

# 3 опция: Вот очевидный путь петли:

my $found_index = -1;
my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
foreach my $index (0..$#strings) {
    next if $search_in !~ /$strings[$index]/;
    $found_index = $index;
    last; # quit the loop early, which is why I didn't use "map" here
}
# Check $found_index against -1; and if you want "3" instead of "2" add 1.
2 голосов
/ 11 июня 2010

Вот решение, которое создает регулярное выражение со встроенным кодом для увеличения индекса при перемещении perl по регулярному выражению:

my @brands = qw( Canon HP Sony );
my $string = "Sony's Cyber-shot DSC-S600";

use re 'eval';  # needed to use the (?{ code }) construct

my $index = -1;
my $regex = join '|' => map "(?{ \$index++ })\Q$_" => @brands;

print "index: $index\n" if $string =~ $regex;

# prints 2 (since Perl's array indexing starts with 0)

Строка, которая предшествует каждой марке, сначала увеличивает индекс, а затемпытается сопоставить марку (экранируется с quotemeta (как \Q), чтобы разрешить регулярное выражение специальных символов в названиях брендов).

Когда совпадение не удается, механизм регулярных выражений проходит чередование |, а затем шаблон повторяется.

Если у вас есть несколько строк для сравнения, обязательно сбросьте $index передкаждый.Или вы можете добавить (?{$index = -1}) к строке регулярного выражения.

1 голос
/ 11 июня 2010

Самый простой способ - использовать хеш и регулярные выражения:

my $search = "your search string";
my %translation = (
    'canon' => 1,
    'hp'    => 2,
    'sony'  => 3
);

for my $key ( keys %translation ) {
    if ( $search =~ /$key/i ) {
        return $translation{$key};
    )
}

Естественно, что возвращение так же легко может быть печатью. Вы также можете окружить все это в цикле while:

while(my $search = <>) {
    #your $search is declared = to <> and now gets its values from STDIN or strings piped to this script
}

Также ознакомьтесь с возможностями регулярных выражений Perl на perlre и посмотрите на структуры данных perl на perlref

EDIT

как мне только что сказали, вы пытались уклониться от использования петли. Другой метод - использовать функцию отображения Perl. Взгляните здесь .

0 голосов
/ 11 июня 2010

Вы также можете взглянуть на Regexp :: Assemble , который возьмет коллекцию под Регулярных выражений и создаст из них один супер-регулярное выражение, которое затем можно будет использовать для проверки всех сразу (и дает вам текст, который соответствует регулярному выражению, конечно). Я не уверен, что это лучшее решение, если вы смотрите только на три строки / регулярные выражения, которые вы хотите сопоставить, но это определенно путь, если у вас значительно больший набор целей - проект, в котором я изначально использовал его имеет библиотеку из примерно 1500 терминов, с которыми он сопоставляет данные, и работает очень хорошо.

...