Самый простой способ соответствовать массиву строк для поиска в жемчуге?

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

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

Reference Array: (1 = 'Canon', 2 = 'HP', 3 = 'Sony')
Search String: Sony's Cyber-shot DSC-S600
End Result: 3
7
задан Ben Dauphinee 13 June 2010 в 13:22
поделиться

4 ответа

UPDATE:

По результатам обсуждения в этом вопросе, в зависимости от ваших намерений/критериев того, что такое "не использовать цикл", решение на основе map (см. "Вариант #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

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

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

NOTE: Я лично считаю это некоторым жульничеством, потому что для реализации join() Perl сам должен сделать цикл где-то внутри интерпета. Так что этот ответ может не удовлетворить ваше желание остаться без цикла, в зависимости от того, хотели ли вы избежать цикла по соображениям производительности или для того, чтобы иметь более чистый или короткий код.


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

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

#1 вариант: - мой любимый. Использует "карту", которую я лично все еще считаю петлей:

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 вариант: Использует цикл, скрытый за хорошим методом библиотеки 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.
11
ответ дан 6 December 2019 в 15:18
поделиться

Простой способ - использовать хэш и regex:

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
}

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

EDIT

Как мне только что указали, вы пытались уйти от использования цикла. Другим методом было бы использование функции map в perl. Посмотрите здесь.

1
ответ дан 6 December 2019 в 15:18
поделиться

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

0
ответ дан 6 December 2019 в 15:18
поделиться

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

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), чтобы позволить regex специальные символы в названиях брендов).

При неудачном совпадении механизм regex переходит к чередованию |, после чего шаблон повторяется.

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

2
ответ дан 6 December 2019 в 15:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: