Codebits
that rock!

Aug 15, 2012

Learning perl: regular expressions

posted by varnie • Tags: perlShow comments

Неторопливо изучаю великую книгу Learning Perl авторов Randal L. Schwartz, Tom Phoenix, brian d foy и пробую на практике изученное.

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

“определить, содержит ли указанная строка элементы, разделенные одним и тем же разделителем и если да - вернуть разделитель. Два разделителя не могут идти одновременно.”

Т.е к примеру для строки q,w,e,r,t,y результатом должна быть запятая.

Для строки qwe!qer2452!wt - восклицательный знак.

Для строки a(b(( - ответ должен быть отрицательным, т.к. после второго разделителя, (, нет элемента и сразу идет третий разделитель. Можно было изменить условие задачи и считать такие строки подходящими, но я решил что такие ситуации не слишком полезны на практике.

И да, как я уже сказал, строка вида 234@3252@foobar! не должна удовлетворять условиям задачи - т.к. здесь присутствуют 2 различных разделителя.

Итак, моё решение:

#!/usr/bin/perl
use Modern::Perl;

print "please enter a test string, or q/Q for exit: ";
chomp(my $input = <STDIN>);
 
while (lc($input) ne "q") { 
    my $separator = find_separator($input);
    
    print "correct. separator is `$separator`\n" if $separator;
    print "please enter a test string, or q/Q for exit: ";
    chomp($input = <STDIN>);
}

sub find_separator {
    my $str = shift @_;
    
    my ($result, $separator);
    while ($str =~ s/^\w+(?<SEPARATOR>\W)//) {
        if ($separator && $separator ne $+{SEPARATOR}) {
            $result = 0;
            last;
        }
       
        ($separator, $result) = ($1, 1);
    }
   
    if (length $str > 0 && $str !~ /^\w+$/) {
        $result = 0;
    }
    
    $result ? $separator : undef;
}

Если мы усложним немного условие задачи, запретив строки оканчивающиеся на разделитель (к примеру, foo*), то потребуется чуть-чуть изменить подпрограмму find_separator:

sub find_separator {
    my $str = shift;
    
    my ($result, $separator);
    while ($str =~ s/^\w+(?<SEPARATOR>\W)//) {
        if ($separator && $separator ne $+{SEPARATOR} || length $str == 0) {    #added || length $str == 0
            $result = 0;
            last;
        }
       
        ($separator, $result) = ($1, 1);
    }
   
    if (length $str > 0 && $str !~ /^\w+$/) {
        $result = 0;
    }
    
    $result ? $separator : undef;
}

Для вышеуказаной работы с подпрограммой find_separator этого достаточно, но что если мы попытаемся вызвать её без передачи строки? Мы получим warning:

Use of uninitialized value $str in substitution (s///) at /home/varnie/foo/regexp.pl.

Для защиты от подобных неприятностей немного допилим подпрограмму find_separator:

sub find_separator {
    return undef unless @_ > 0; #added
    
    my ($str, $result, $separator) = shift;
    while ($str =~ s/^\w+(?<SEPARATOR>\W)//) {
        if ($separator && $separator ne $+{SEPARATOR} || length $str == 0) {
            $result = 0;
            last;
        }
       
        ($separator, $result) = ($1, 1);
    }
   
    if (length $str > 0 && $str !~ /^\w+$/) {
        $result = 0;
    }
    
    $result ? $separator : undef;
}

Теперь, даже если вызовем find_separator() без передачи ей строки, она отработает правильно.


blog comments powered by Disqus
Fork me on GitHub