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

Я выполнял обзор кода для коллеги, и у него было регулярное выражение, которое было похоже на это:

if ($value =~ /^\d\d\d\d$/) {
    #do stuff
}

Я сказал ему, что он должен изменить его на:

if ($value =~ /^\d{4}$/) {
    #do stuff
}

На который он ответил, что предпочел первое для удобочитаемости (я нахожу второе более читаемое, но это - религиозные дебаты, которые я сохраню в течение другого дня).

Мой вопрос: существует ли фактическое преимущество для одного по другому?

10
задан Morinar 30 March 2010 в 18:23
поделиться

9 ответов

Они делают то же самое, но с точки зрения практичности это вопрос предпочтений. Есть ли небольшая разница в производительности так или иначе? Кто знает, но это уж точно несущественно.

Кванторы более полезны (и требуются), когда длина шаблона не фиксирована, например \ d {12,16} , \ d {2,} , и т. д.

Я предпочитаю \ d {4} , который мне легче разобрать, чем \ d \ d \ d \ d

И что, если вы сопоставляете символ класс, а не простая цифра? [aeiouy0-9] {4} или [aeiouy0-9] [aeiouy0-9] [aeiouy0-9] [aeiouy0-9] ?

12
ответ дан 3 December 2019 в 13:29
поделиться

Лучше думать, что когда он хочет найти набор из 10+ букв он должен будет использовать квантификатор, а не повторение, лучше привыкнуть к правильному пути, кроме того, если он настаивает на использовании повторения для больших наборов символов ters, у кого-то возникнут проблемы при попытке их подсчета, которые не потребовались бы, если бы они были помечены квантификатором.

3
ответ дан 3 December 2019 в 13:29
поделиться

Я просто сейчас обойду вопрос о удобочитаемости.

Сначала давайте посмотрим, до чего компилируется каждая версия.

perl -Mre=debug -e'/^\d{4}$/'
Compiling REx "^\d{4}$"
synthetic stclass "ANYOF[0-9][{unicode_all}]".
Final program:
   1: BOL (2)
   2: CURLY {4,4} (5)
   4:   DIGIT (0)
   5: EOL (6)
   6: END (0)
anchored ""$ at 4 stclass ANYOF[0-9][{unicode_all}] anchored(BOL) minlen 4 
Freeing REx: "^\d{4}$"
perl -Mre=debug -e'/^\d\d\d\d$/'
Compiling REx "^\d\d\d\d$"
Final program:
   1: BOL (2)
   2: DIGIT (3)
   3: DIGIT (4)
   4: DIGIT (5)
   5: DIGIT (6)
   6: EOL (7)
   7: END (0)
anchored ""$ at 4 stclass DIGIT anchored(BOL) minlen 4 
Freeing REx: "^\d\d\d\d$"

Теперь я хочу посмотреть, насколько хорошо работает каждая версия.

#! /usr/bin/env perl
use Benchmark qw':all';

cmpthese( -10, {
  'loop' => sub{ 1234 =~ /^\d{4}$/ },
  'repeat' => sub{ 1234 =~ /^\d\d\d\d$/ }
});
           Rate   loop repeat
loop   890004/s     --   -10%
repeat 983825/s    11%     --

Хотя / ^ \ d \ d \ d \ d $ / действительно работает быстрее, это ненамного быстрее. Что на самом деле просто оставляет его на удобочитаемости.


Давайте доведем этот пример до крайности:

/^\d{32}$/;
/^\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d$/;

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

Если мы перейдем к другой крайности, первый стиль покажется совершенно излишним.

/^\d{1}$/;
/^\d$/;

На самом деле все сводится к тому, сколько повторений \ d , прежде чем ваше предпочтение переключится с простого повторения \ d на использование квантификатора.

10
ответ дан 3 December 2019 в 13:29
поделиться

Как и многое другое, это вопрос того, насколько далеко вы хотите зайти.

Реальный пример.

Сравните:

my @lines = $header =~ m/([^\n\r]{13}|[^\n\r]+)/g; #split header into groups of up to 13 characters

с

my @lines = $header =~ m/([^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r]|[^\n\r]+)/g; #split into groups of up to 13 characters

Сможете ли вы найти трубку '|'?

1
ответ дан 3 December 2019 в 13:29
поделиться

О удобочитаемости ... Программисты Perl используют очень редкие функции, надеясь, что они будут удобочитаемыми, однако для этого требуется понимание этой редкой функции.

Есть много новичков в регулярных выражениях, которые не понимают, что такое {4}.

Что касается преимуществ, второй вариант может быть лучше, потому что он требует меньше элементов массива в механизме регулярных выражений. Если вы не настоящий программист, вы не сможете оптимизировать производительность до наносекунд.

-1
ответ дан 3 December 2019 в 13:29
поделиться

С первого взгляда будет сложно сосчитать любое повторение более 3 или 4 штук. Считаю это веской причиной. Кроме того, использование квантификатора - это более «плотный» способ выразить повторяющуюся информацию. Для меня это как разница между «повторным использованием» кода копирования и вставки и написанием действительно повторно используемого кода.

5
ответ дан 3 December 2019 в 13:29
поделиться

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

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

Абсолютный квантор, такой как {4} , просто проще указать и передать другим программистам. Кто хочет подсчитать количество \ d s вручную? Вы пишете код для чтения другими людьми, поэтому не усложняйте им жизнь.

Однако вы могли пропустить ошибку в этом коде, потому что сосредоточились на проблеме квантификатора. Якорь $ разрешает новую строку в конце строки, и если появляется фанатик Perl Best Practices и вслепую добавляет / xsm ко всем регулярным выражениям (a болезненный опыт, который я видел более чем несколько раз), что $ допускает еще более неверный вывод. Возможно, вам понадобится абсолютная привязка конца строки \ z .

Не то, чтобы это произошло в вашем случае, но проверки кода, как правило, превращаются в проверки стиля или синтаксиса (потому что их легче заметить) и фактически упускают из виду точку проверки правильного и предполагаемого поведения и правильного дизайна. Часто проблемы стиля не стоят того, чтобы беспокоиться о рассмотрении всех других способов, которыми вы могли бы потратить время на улучшение кода. :)

15
ответ дан 3 December 2019 в 13:29
поделиться

Я мог бы использовать любую форму, в зависимости от обстоятельств.

Давайте проигнорируем непонятную сложность пользовательских классов символов, повторяющихся 96 раз все в одной строке, и вместо этого сосредоточимся на хорошо написанном коде.

Обратите внимание:

$foo =~ m{
        (\d\d\d\d)
    [ ] (\d\d\d?)
    [ ] (\w\w)
}x;

Я использовал подобный код для анализа данных с датчиков погоды. Я использую этот формат, потому что он полностью соответствует документации производителя. Это очень хорошо работает для форматов данных «фиксированной ширины», которые не совсем соответствуют обещаниям полей фиксированной ширины (на практике это очень распространено).

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

В других случаях я использовал такой код:

$foo =~ m{ 
        ( \d{4}   )
    [ ] ( \d{2,3} )
    [ ] ( \w{2}   )
}x;

Чтобы сохранить читаемость вышеупомянутого, вам нужно добавить больше пробелов и немного поиграть с форматированием.

Второй стиль лучше масштабируется по сложности - добавление пользовательских классов символов и широких полей не нарушает удобочитаемость.

Самая важная вещь - согласованность в данном регулярном выражении.IOW, никогда не делайте этого:

$foo =~ m{ 
        ( \d\d\d\d )
    [ ] ( \d{2,3}  )
    [ ] ( \w\w     )
}x;

В конечном счете, код выполняет две функции. Самая известная функция заключается в том, что она сообщает компьютеру, что делать. Но самая важная, но в значительной степени упускаемая из виду функция кода - это сообщать программисту , что делает компьютер.

0
ответ дан 3 December 2019 в 13:29
поделиться

{4} легче поддерживать, чем \ d \ d \ d \ d , потому что он лучше масштабируется. Например, если вам позже потребуется изменить его, чтобы он соответствовал 11 цифрам, вы можете просто изменить 4 на 11, вместо того, чтобы добавлять 14 символов в ваше регулярное выражение.

2
ответ дан 3 December 2019 в 13:29
поделиться
Другие вопросы по тегам:

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