2017-02-03 3 views
2

Я работаю над программой chet-bot в perl и регулярном выражении, но я не получаю желаемых результатов, поскольку вы видите, что у меня есть все местоимения и глаголы в хеше, и я прокручиваю строку, и если она соответствует хеш-ключу, то замените хэш-ключ значение с текущим значением подстроки.соответствие шаблону и регулярное выражение в perl?

вывод программы
Элиза: Привет, я психотерапевт. Как вас зовут?
адам: меня зовут адам
Элиза: Здравствуйте адам, как вы?
адам: я чувствую себя плохо
Элизу: Почему я чувствую себя плохо?
адам:, потому что я болен
Элиза: Почему, потому что вы болен?

независимо от «потому что» слово в последнем вопросе, но выход должен быть что-то вроде этого

Элизы: Почему, потому что вы больны?
любые предложения о том, как я могу решить эту проблему.

код:

sub makeQuestion{ 
    my ($patient) = @_; 
    my %reflections = (
     "am" => "are ", 
     "was" => "were ", 
     "i"  => "you ", 
     "i'd" => "you would ", 
     "i've" => "you have ", 
     "i'll" => "you will ", 
     "my" => "your ", 
     "are" => "am ", 
     "you've"=> "I have ", 
     "you'll"=> "I will ", 
     "your" => "my ", 
     "yours" => "mine ", 
     "you" => "me ", 
     "me" => "you " 
    ); 

    my @toBes = keys %reflections; 
    foreach my $toBe (@toBes) { 
     if ($patient =~/$toBe\b/) 
     { 
      $patient=~ s/$toBe /$reflections{$toBe}/i; 

     } 

    } 
    print "Why $patient? \n"; 
} 

ответ

2

EDIT: Как было предложено @zdin, я заменяю '\s' на ' ' который будет соответствовать любое количество пробелов, а также игнорировать ведущие пробельные.

Это потому, что вы перебираете полный ключ вашего хэша% reflections и выполняете систематические замены. Поэтому вы находите ключ «am» в цикле 1 и заменяете его «are». Затем вы найдете ключ «есть» в цикле 8 и замените его «am».

Как сделать одиночные замены слов, вы должны обеспечить достаточно запустить над одним словом только один раз с помощью раскола:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $question = ''; 

while ($question ne 'stop') { 
    $question = <STDIN>; 
    chomp $question; 
    print makeQuestion($question)."\n"; 
} 

sub makeQuestion{ 
    my ($patient) = @_; 
    my @new_question; 
    my %reflections = (
     "am" => "are", 
     "was" => "were", 
     "i"  => "you", 
     "i'd" => "you would", 
     "i've" => "you have", 
     "i'll" => "you will", 
     "my" => "your", 
     "are" => "am", 
     "you've"=> "I have", 
     "you'll"=> "I will", 
     "your" => "my", 
     "yours" => "mine", 
     "you" => "me", 
     "me" => "you", 
    ); 
    WORDS: foreach my $word (split ' ', $patient) { 
     REPLACE: foreach my $key (keys %reflections) { 
      if ($word =~ m{\A$key\Z}i) { 
       $word =~ s{$key}{$reflections{$key}}i; 
       last REPLACE; 
      } 
     } 
     push @new_question, $word; 
    } 
    return join ' ', @new_question; 
} 
+0

Это хороший подход к его гибкости. Его очень легко настроить, когда появятся новые потребности. Несколько комментариев. Для первого тестирования (с совпадением регулярных выражений) нет большой пользы для обработки (с заменой регулярного выражения) - если 's /' не соответствует, это не изменит его цель. Я думаю, что в этом случае '\ b' хватит в качестве якоря, и эти метки цикла делают его, возможно, менее читабельным (конечно, как вопрос вкуса). Однако они, безусловно, полезны в качестве демонстраций. – zdim

+1

На самом деле, поскольку вы хорошо «раскалываете» слова сначала, нет оснований для привязки, поскольку слово проверено на слово. Но я предлагаю разделить любое количество пробелов, чтобы быть в безопасности. По умолчанию это шаблон '' ''(или, конечно, эквивалент'/\ s */'). Эти комментарии не предназначены для умаления ответа, который, я считаю, полезен и полезен :) – zdim

+0

Спасибо за разъяснения zdin. Однако именно там я встречаю свои лимиты в perl: я хочу избежать множественных подстановок, поэтому я даю тест на месте: чтобы быть уверенным, что одно слово заменяется только один раз. Если я не использую этот тест, как я узнаю, что произошла замена, а затем остановить цикл, чтобы перейти к следующему слову? –

3

Ваш код делает круговые замены, так как он всегда обрабатывает всю фразу. Он заменяет слово, а затем заменяет заменой. answer by David Verdin объясняет это и показывает способ исправить это.

Вот еще один способ

my $phrase = join ' ', map { $reflections{$_} // $_ } split ' ', $patient; 

Список слов, полученных split подается в map, который применяется код в блоке к каждому. Внутри блока текущий обработанный элемент находится по умолчанию $_ variable.

Если слово является ключом в хеше с значением defined, то это значение возвращается, иначе само слово возвращается. Это достигается //, defined-or operator. Таким образом, все слова, имеющие хэш-ключ, заменяются соответствующими значениями, а другие передаются без изменений. Их порядок сохраняется, поэтому мы получаем наш список слов, обрабатываемых по мере необходимости.

Этот список вывода затем присоединяется пространство, образуя фразу, которая должна предшествовать по 'Why '

Обратите внимание, что шаблон ' ' в split совпадает с любым количеством любых пробелов. Он обычно используется для разрыва строк «по пространству» (в «слова») и имеет значение split.


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

foreach my $item (@list) { 
    $item =~ s/$pattern/$repl/; 
} 

и если $pattern не соответствует в $item происходит ничего, то $item не меняется.

+0

Этот ответ намного холоднее моего. Я передо мной. –

+0

@DavidVerdin Ну, спасибо. Хотелось бы отметить, что эти процедурные, поэтапные решения (как и ваши) имеют явные преимущества. Во-первых, они более гибкие - можно просто добавить код, поскольку требуется дополнительная обработка. Когда он складывается на «карте», может быть труднее настроить вещи. – zdim

0

Это происходит потому, что в конкретной фразе/предложении есть два подходящих кандидата в соответствии с вашим регулярным выражением. А также, факт, что элементы хэшей упорядочены случайным образом в памяти. В вашем коде вы только что получили ключи от %refelections, вы не указали на его сортировку. Поэтому каждый запуск, keys %reflections будет возвращать массив различной сортировки. Например, в Run 1 это может быть ('am', 'i', 'my', me' ...), затем следующий запуск ('i', "you'll', 'my', 'yours').

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