В Perl, какво е значението на if (s/^\+//)?

В Perl/Tk код намерих условен израз, както е показано по-долу

if (s/^\+//)
{
   #do something
}
elsif (/^-/)
{
   #do another thing
}

Изглежда, че е направено съпоставяне на шаблони. Но не мога да го разбера. Може ли някой да ми помогне да разбера това съвпадение на шаблон?


person Kadiam    schedule 22.11.2012    source източник
comment
@Oded, тази връзка не е най-лошият урок за Perl, който някога съм виждал, но е доста стар и използва няколко остарели практики (-w срещу warnings, прототипи). Може да препоръчам perldoc perlretut.   -  person Joel Berger    schedule 22.11.2012
comment
@JoelBerger - Честно. Това беше първото попадение в Google.   -  person Oded    schedule 22.11.2012
comment
@Одед, НП. Дълголетието на уроците по Perl е нещо, с което общността се опитва да се бори, като прави/маркира по-новите по-добри и предупреждава за стари/лоши perl-tutorial.org. Perl получи лошо име, след като бумът на dot-com донесе много лош код, ние се опитваме да го направим по-добър един урок в даден момент.   -  person Joel Berger    schedule 22.11.2012
comment
Добавих този урок към списъка с наследени ръководства на perl-tutorial.org. Прекарва твърде много време върху някакъв стар синтаксис, но върши прилична работа с докосването на новия. Не е лошо, но не е най-доброто за начинаещ.   -  person Joel Berger    schedule 22.11.2012
comment
Какво направихте, за да се опитате да разберете този код? Документацията, която идва с Perl, е наистина изчерпателна. Какво ви попречи да намерите отговорите там?   -  person Dave Cross    schedule 23.11.2012


Отговори (4)


И двата са регулярни изрази. Можете да прочетете за тях на perlre и perlretut. Можете да си поиграете с тях на http://www.rubular.com/r/CEtqZ3YUIi.

И двамата имплицитно правят нещо с $_. Вероятно има while или foreach около вашите редове код без променлива за цикъл. В този случай $_ става тази променлива на цикъла. Може например да съдържа текущия ред на файл, който се чете.

  1. Ако текущата стойност на $_ съдържа знак + (плюс) като първи знак в началото на низа, #do somehting.
  2. В противен случай, ако съдържа знак - (минус), #do another thing.

В случай 1. той също замества знака + с нищо (т.е. премахва го). Това обаче не премахва - във 2.


Нека да разгледаме обяснение с YAPE::Regex::Explain.

use YAPE::Regex::Explain;
print YAPE::Regex::Explain->new(qr/^\+/)->explain();

Ето го. Не е много полезно в нашия случай, но все пак е хубав инструмент. Имайте предвид, че частите (?-imsx и ) са нещата по подразбиране, които Perl предполага. Винаги са там, освен ако не ги смените.

The regular expression:

(?-imsx:^\+)

matches as follows:

NODE                     EXPLANATION
----------------------------------------------------------------------
(?-imsx:                 group, but do not capture (case-sensitive)
                         (with ^ and $ matching normally) (with . not
                         matching \n) (matching whitespace and #
                         normally):
----------------------------------------------------------------------
  ^                        the beginning of the string
----------------------------------------------------------------------
  \+                       '+'
----------------------------------------------------------------------
)                        end of grouping
----------------------------------------------------------------------

Актуализация: Както Mikko L посочи в коментарите, може би трябва да преработите/промените тази част от кода. Въпреки че вероятно прави това, което трябва, вярвам, че би било добра идея да го направите по-четлив. Който и да го е написал, очевидно не го е грижа за вас като по-късен поддържащ. Предлагам ви да го направите. Можете да го промените на:

# look at the content of $_ (current line?)
if ( s/^\+// )
{
  # the line starts with a + sign,
  # which we remove!

  #do something
}
elsif ( m/^-/ )
{
  # the line starts witha - sign
  # we do NOT remove the - sign!

   #do another thing
}
person simbabque    schedule 22.11.2012
comment
Вашият анализ е на място. Бих добавил, че ОП трябва да обмисли рефакторинг на тази част от кода, така че заместването да е по-очевидно. Много е лесно да го пропуснете така, както е написано сега. Дори просто да отидете до if( /^\+/ ) { $_ =~ s/^\+//; # do something } би помогнало значително. - person Mikko L; 22.11.2012
comment
@MikkoL Съгласен съм и добавих предложенията. Не съм съгласен с изричното използване на $_, но според мен това е лично предпочитание. Благодаря ти. - person simbabque; 22.11.2012
comment
Гласуващ против, искаш ли да коментираш как мога да подобря публикацията си? - person simbabque; 22.11.2012
comment
Не съм толкова голям фен на този рефакторинг. Добавя друго съвпадение на шаблон, а съвпадението на шаблон може да бъде доста скъпо в някои случаи. Също така е доста нормален идиом на Perl да се използва if (s/.../.../), за да се провери дали е извършено заместване. Ако се притеснявате за яснотата, коментарът би свършил работа (забележка: аз не гласувам против – като цяло отговорът е добър). - person dan1111; 22.11.2012
comment
Съгласен съм с Dan, няма причина да не s/// както да съпоставите, така и да премахнете маркер. Ето защо моделите връщат броя на съвпаденията/заместванията в скаларен контекст. - person Joel Berger; 22.11.2012
comment
Като цяло и аз съм съгласен. Мисля обаче, че коментарът на Mikko L е добър. Реших да го използвам, защото OP очевидно не е много силен с Perl, така че реших, че полезността на изричния вариант с допълнително съвпадение надвишава цената на това друго съвпадение. ще го сменя - person simbabque; 22.11.2012

Това са регулярни изрази, използвани за съвпадение на шаблони и заместване.

Трябва да прочетете концепцията, но що се отнася до вашия въпрос:

s/^\+//

Ако низът започва с плюс, премахнете този плюс ("s" означава "заместване") и върнете true.

/^-/

Вярно, ако низът започва с минус.

person Thilo    schedule 22.11.2012

Този код е еквивалентен на

if ($_ =~ s/^\+//) {  # s/// modifies $_ by default
   #do something
}
elsif ($_ =~ m/^-/) {  # m// searches $_ by default
   #do another thing
}

s/// и m// са подобни на кавички оператори на регулярен израз. Можете да прочетете за тях в perlop.

person Eugene Yarmash    schedule 22.11.2012
comment
Това е напълно добър (макар и кратък) отговор. Не виждам защо беше гласувано против. - person simbabque; 22.11.2012
comment
Не гласувах против, но съм сигурен, че това е така, защото имплицитната променлива не е под въпрос тук, а по-скоро самият модел на заместване. - person Joel Berger; 22.11.2012

Другите отговори дадоха обобщение на това как работи кодът, но не много защо. Ето един прост пример защо човек може да използва такава логика.

#!/usr/bin/env perl

use strict;
use warnings;

my $args = {};

for ( @ARGV ) {
  if ( s/^no// ) {
    $args->{$_} = 0;
  } else {
    $args->{$_} = 1;
  }
}

use Data::Dumper;
print Dumper $args;

Когато извикате скрипта като

./test.pl hi nobye

Вие получавате

$VAR1 = {
          'hi' => 1,
          'bye' => 0
        };

Ключът е низът, но ако е предшестван от no, тогава го премахнете (за да получите въпросния ключ) и вместо това задайте стойността на 0.

Примерът на OP е малко по-ангажиращ, но следва същата логика.

  • ако ключът започва с +, премахнете го и направете нещо
  • ако ключът започва с -, не го премахвайте и направете нещо друго
person Joel Berger    schedule 22.11.2012