Переписывает старую программу, используя устаревший $ * - PullRequest
7 голосов
/ 30 сентября 2019

У нас есть действительно старый Perl-код, последний раз обновленный в 1997 году. Я пытаюсь перейти на более новую версию Perl, где $* устарела.

Я пытаюсь научиться переписывать это, ноединственная помощь, которую вы получите из документации по perlvar: «Вместо этого вы должны использовать модификаторы регулярных выражений / s и / m».

  my ($file, $regexp, $flags) = @_;
  my (@found_lines, @tmp_list, $comp_buf);
  local ($*);

  if ($flags =~ tr/c//d)
  {
    $* = 1;
    (substr ($regexp, 0, 1) ne "^") && ($regexp = "^.*$regexp");
    ($regexp !~ /([^\\]|^)(\\\\)*\$$/) && ($regexp .= ".*\$");
    &read_comp ($file, \$comp_buf);
    @found_lines = grep ($_ .= "\n", ($comp_buf =~ /$regexp/g));
  }
  else
  {
    @tmp_list = &read_list ($file, 0);
    @found_lines = grep (/$regexp/, @tmp_list);
  }

  if ($flags eq "q")
  {
    $#found_lines >= 0;
  }
  elsif ($flags eq "a")
  {
    $#found_lines+1;
  }
  else
  {
    @found_lines;
  }

Для меня действительно трудно понять, как заменить $* здесь, насколько я понимаю из комментариев, которые мы используем $* здесь, чтобы включить многострочное сопоставление для следующего поиска регулярных выражений. Поэтому я предполагаю, что мне нужно как-то добавить эти флаги в выражения регулярного выражения.

Как мне переписать этот код, чтобы заменить существующие $* экземпляры?

1 Ответ

10 голосов
/ 30 сентября 2019

К сожалению, $* является глобальной переменной, поэтому ее установка влияет на все вызываемые функции (например, read_comp), если они используют регулярные выражения.

Кроме того, этот код написан несколько странным образом. :

  • Я предполагаю, что намерение состояло в том, чтобы включить «многострочное» сопоставление для части $comp_buf =~ /$regexp/g, но $* устанавливается рано, поэтому это также влияет на $regexp !~ /([^\\]|^)(\\\\)*\$$/ и read_comp call.

  • Проверка того, является ли $regexp уже начинающимся / заканчивающимся с ^ / $ соответственно, прервана. Например, (?:^foo$) является привязанным регулярным выражением, но код не обнаружит этого.

  • grep ($_ .= "\n", ...) является недопустимым злоупотреблением grep для эмуляции map. Код пытается получить список строк, совпадающих с регулярным выражением. Тем не менее, способ построения регулярного выражения не соответствует завершающему символу новой строки "\n" в каждой строке, поэтому код вручную добавляет "\n" к каждой возвращаемой строке.

    Разумный способ сделать это будет:

    @found_lines = map $_ . "\n", ...;   # or map "$_\n", ...
    

    Вместо map мы могли бы использовать императивный цикл, воспользовавшись тем, что for связывает переменную цикла с текущим элементом списка:

    @temp = ...;
    for (@temp) {
        $_ .= "\n";
    }
    @found_lines = @temp;
    

    Вместо цикла for мы могли бы использовать grep для побочного эффекта итерации по списку:

    @temp = ...;
    grep $_ .= "\n", @temp;
    @found_lines = @temp;
    

    grep также псевдонимы $_ для текущего элемента, поэтому выражение фильтра"может изменить список, который мы перебираем.

    Наконец, поскольку .= возвращает результирующую строку (а строки, содержащие "\n", не могут быть ложными), мы можем воспользоваться тем, что наш" фильтр "выражение "всегда возвращает истинное значение и эффективно получает копию списка ввода в качестве возвращаемого значения из grep:

    @found_lines = grep $_ .= "\n", ...  # blergh
    

Что касается эффекта $*: Этоэто логический флаг (вошибочно)Если установлено значение true, все регулярные выражения ведут себя так, как если бы действовал /m, то есть ^ и $ совпадают во встроенных символах новой строки, а также в начале / конце строки.

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

  • local ($*); можно удалить.
  • $* = 1; также необходимо перейти.
  • $comp_buf =~ /$regexp/g следует изменить на $comp_buf =~ /$regexp/mg. Это единственное место, где я вижу, где имеет смысл многострочный режим.
  • Я бы очень хотел переписать последнюю строку. Либо

    @found_lines = map "$_\n", ($comp_buf =~ /$regexp/g);
    

    (функциональный стиль), либо, если вы предпочитаете более императивный стиль:

    @found_lines = ($comp_buf =~ /$regexp/g);
    $_ .= "\n" for @found_lines;
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...