Есть ли веская причина использовать квантификаторы в регулярных выражениях Perl вместо простого повторения символа? - PullRequest
10 голосов
/ 30 марта 2010

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

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

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

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

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

Мой вопрос: есть ли реальная выгода для одного перед другим?

Ответы [ 9 ]

15 голосов
/ 30 марта 2010

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

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

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

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

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

12 голосов
/ 30 марта 2010

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

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

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

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

10 голосов
/ 31 марта 2010

Сейчас я просто собираюсь обойти проблему читабельности.

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

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 на использование квантификатора.

5 голосов
/ 30 марта 2010

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

3 голосов
/ 30 марта 2010

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

2 голосов
/ 31 марта 2010

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

1 голос
/ 31 марта 2010

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

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

Сравнить:

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

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

0 голосов
/ 31 марта 2010

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

Давайте не будем обращать внимания на сложность пользовательских классов символов, повторяемых 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 голосов
/ 31 марта 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...