Все программисты, похоже, согласны с тем, что читаемость кода гораздо важнее, чем однострочные с коротким синтаксисом, которые работают, но требуют, чтобы старший разработчик интерпретировал их с какой-то степенью точности - но, похоже, именно так были разработаны регулярные выражения. Была ли причина для этого?
Мы все согласны с тем, что selfDocumentingMethodName()
гораздо лучше, чем e()
. Почему это не относится и к регулярным выражениям?
Мне кажется, что вместо разработки синтаксиса однострочной логики без структурной организации:
var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
И это даже не строгий анализ URL!
Вместо этого мы могли бы сделать некоторую структуру конвейера организованной и читаемой для базового примера:
string.regex
.isRange('A-Z' || 'a-z')
.followedBy('/r');
Какое преимущество дает чрезвычайно краткий синтаксис регулярного выражения, кроме как самого короткого из возможных операций и логического синтаксиса? В конечном счете, существует ли конкретная техническая причина плохой читаемости синтаксического дизайна регулярных выражений?
источник
Ответы:
Существует одна большая причина, по которой регулярные выражения были спроектированы такими же краткими, как и они: они были разработаны для использования в качестве команд для редактора кода, а не в качестве языка для кодирования. Точнее, это
ed
была одна из первых программ, использующих регулярные выражения и оттуда регулярные выражения начали свое завоевание мирового господства. Например,ed
командаg/<regular expression>/p
вскоре создала отдельную программу под названиемgrep
, которая используется до сих пор. Благодаря своей мощи они впоследствии были стандартизированы и использованы в различных инструментах, таких какsed
иvim
Но хватит мелочи. Итак, почему это происхождение предпочитает краткую грамматику? Потому что вы не вводите команду редактора, чтобы прочитать ее еще раз. Достаточно того, что вы можете вспомнить, как собрать все вместе, и что вы можете делать с ним то, что хотите. Тем не менее, каждый символ, который вы вводите, замедляет процесс редактирования вашего файла. Синтаксис регулярных выражений был разработан для написания относительно сложных поисков одноразовым способом, и именно это доставляет людям головные боли, которые используют их в качестве кода для анализа некоторого ввода в программу.
источник
grep
это неправильно произносимое слово «схватить», оно на самом деле происходит отg
/re
(для регулярного выражения) /p
?<aaa bbb="ccc" ddd='eee'>
, в нем нет вложенных тегов. Вы не можете вкладывать теги, что вы НЕСТ элементы (открытые теги, содержание , включая дочерние элементы, закрывающий тег), который этот вопрос не спрашивающих о разборе. HTML- теги являются обычным языком - балансировка / вложение происходит на уровне выше тегов.Регулярное выражение, которое вы цитируете, - ужасный беспорядок, и я не думаю, что кто-то согласится с тем, что оно читаемо В то же время, большая часть этого безобразия присуща решаемой проблеме: существует несколько уровней вложения, а грамматика URL-адреса относительно сложна (безусловно, слишком сложна, чтобы общаться кратко на любом языке). Тем не менее, это правда, что есть лучшие способы описать то, что описывает это регулярное выражение. Так почему они не используются?
Большая причина в инерции и вездесущности. Во-первых, это не объясняет, как они стали настолько популярными, но теперь, когда они есть, любой, кто знает регулярные выражения, может использовать эти навыки (с очень небольшим количеством различий между диалектами) на сотне разных языков и еще тысяче программных инструментов ( например, текстовые редакторы и инструменты командной строки). Кстати, последний не будет и не сможет использовать какое-либо решение, которое сводится к написанию программ , потому что они активно используются не программистами.
Несмотря на это, регулярные выражения часто чрезмерно используются, то есть применяются даже тогда, когда другой инструмент будет намного лучше. Я не думаю, что синтаксис регулярных выражений ужасен . Но это явно намного лучше в коротких и простых шаблонах: архетипический пример идентификаторов в C-подобных языках
[a-zA-Z_][a-zA-Z0-9_]*
может быть прочитан с абсолютным минимумом знания регулярных выражений, и как только эта полоса будет достигнута, она будет и очевидной, и лаконичной. Требовать меньше персонажей - это не плохо, а совсем наоборот. Быть кратким - это добродетель, если вы остаетесь понятными.Есть по крайней мере две причины, по которым этот синтаксис превосходит простые шаблоны, подобные этим: он не требует экранирования для большинства символов, поэтому он читает относительно естественно и использует все доступные знаки препинания для выражения различных простых комбинаторов синтаксического анализа. Может быть , самое главное, он не требует вообще ничего для секвенирования. Вы пишете первое, а затем то, что следует за этим. Сравните это с вашим
followedBy
, особенно если следующий шаблон - не буквальное, а более сложное выражение.Итак, почему они терпят неудачу в более сложных случаях? Я вижу три основные проблемы:
Там нет возможности абстракции. Формальные грамматики, которые происходят из той же области теоретической информатики, что и регулярные выражения, имеют набор производств, поэтому они могут давать имена промежуточным частям шаблона:
Как мы могли видеть выше, пробелы, не имеющие особого значения, полезны для того, чтобы сделать форматирование более простым для глаз. То же самое с комментариями. Регулярные выражения не могут этого сделать, потому что пробел - это просто литерал
' '
. Обратите внимание: некоторые реализации допускают «подробный» режим, в котором пропуски игнорируются и возможны комментарии.Нет мета-языка для описания общих моделей и комбинаторов. Например, можно написать
digit
правило один раз и продолжать использовать его в контекстно-свободной грамматике, но нельзя определить, так сказать, «функцию», которая получает продуктp
и создает новый продукт, который делает с ним что-то дополнительное, например, create производство для списка случаев, разделенных запятымиp
.Подход, который вы предлагаете, безусловно, решает эти проблемы. Это просто не решает их очень хорошо, потому что он торгует гораздо более кратко, чем необходимо. Первые две проблемы могут быть решены, оставаясь при этом в относительно простом и лаконичном предметно-ориентированном языке. Третий, ну ... программное решение, конечно, требует языка программирования общего назначения, но, по моему опыту, третий, безусловно, является наименьшей из этих проблем. У немногих шаблонов достаточно вхождений в ту же сложную задачу, которую программист жаждет определить новые комбинаторы. И когда это необходимо, язык часто бывает достаточно сложным, чтобы его нельзя было и не нужно анализировать с помощью регулярных выражений.
Решения для этих случаев существуют. Существует приблизительно десять тысяч библиотек синтаксических анализаторов, которые делают примерно то, что вы предлагаете, просто с другим набором операций, часто с другим синтаксисом и почти всегда с большей мощностью синтаксического анализа, чем регулярные выражения (т. Е. Они имеют дело с контекстно-свободными языками или некоторыми значительными подмножество тех). Кроме того, существуют генераторы синтаксических анализаторов, которые используют подход «лучше использовать DSL», описанный выше. И всегда есть возможность написать часть анализа вручную в правильном коде. Вы можете даже смешивать и сопоставлять, используя регулярные выражения для простых подзадач и делая сложные вещи в коде, вызывая регулярные выражения.
Я не знаю достаточно о первых годах вычислений, чтобы объяснить, как регулярные выражения стали настолько популярными. Но они здесь, чтобы остаться. Вы просто должны использовать их с умом, а не использовать их, когда это будет разумнее.
источник
I don't know enough about the early years of computing to explain how regular expressions came to be so popular.
Однако мы можем рискнуть предположить: базовый механизм регулярных выражений очень прост в реализации, гораздо проще, чем эффективный анализатор без контекста.grep
сравнению с предыдущей версией (версия 3 против версии 4). Похоже, что первое широкое использование регулярных выражений было в 1968 году.yacc
была создана в 1975 году целая идея парсеров LALR (которые были среди первого класса практически используемых парсеров их kind) возникла в 1973 году. В то время как первая реализация движка regexp, скомпилированная JIT-выражениями (!), была опубликована в 1968 году. Но вы правы, трудно сказать, что это заделало, на самом деле трудно сказать, когда регулярные выражения начали «принимать». выкл». Но я подозреваю, что как только они были введены в текстовые редакторы, которые они использовали, они захотели использовать их и в своем программном обеспечении.with very few differences between dialects
Я бы не сказал, что это «очень мало». Любой предопределенный символьный класс имеет несколько определений между разными диалектами. И есть также причуды разбора, определенные для каждого диалекта.Историческая перспектива
Статья в Википедии довольно подробно рассказывает о происхождении регулярных выражений (Kleene, 1956). Оригинальный синтаксис был относительно прост только
*
,+
,?
,|
и группировка(...)
. Это было кратко ( и читабельно, оба не обязательно противоположны), потому что формальные языки, как правило, выражаются в кратких математических обозначениях.Позже, синтаксис и возможности развивались с редакторами и росли с Perl , который пытался быть кратким по замыслу ( «общие конструкции должны быть короткими» ). Это значительно усложнило синтаксис, но учтите, что люди привыкли к регулярным выражениям и умеют их писать (если не читают). Тот факт, что они иногда предназначены только для записи, говорит о том, что когда они слишком длинные, они, как правило, не являются правильным инструментом. Регулярные выражения имеют тенденцию быть нечитаемыми при злоупотреблении.
Помимо строковых регулярных выражений
Говоря об альтернативных синтаксисах, давайте посмотрим на тот, который уже существует ( cl-ppcre , в Common Lisp ). Ваше длинное регулярное выражение может быть проанализировано
ppcre:parse-string
следующим образом:... и результаты в следующей форме:
Этот синтаксис более многословен, и, если вы посмотрите на комментарии ниже, он не обязательно будет более читабельным. Так что не думайте, что поскольку у вас менее компактный синтаксис, все будет автоматически яснее .
Однако, если у вас начнутся проблемы с регулярными выражениями, их преобразование в этот формат может помочь вам расшифровать и отладить код. Это одно преимущество по сравнению со строковыми форматами, где может быть трудно обнаружить ошибку в один символ. Основным преимуществом этого синтаксиса является манипулирование регулярными выражениями с использованием структурированного формата вместо строкового кодирования. Это позволяет вам создавать и создавать такие выражения, как любая другая структура данных в вашей программе. Когда я использую приведенный выше синтаксис, это обычно происходит потому, что я хочу создавать выражения из более мелких частей (см. Также мой ответ на CodeGolf ). Для вашего примера мы можем написать 1 :
Строковые регулярные выражения также могут быть составлены с использованием конкатенации строк или интерполяции, заключенной в вспомогательные функции. Тем не менее, существует ограничение с строковыми манипуляциями , которые имеют тенденцию загромождать в код (думает о вложенности проблемы, а не в отличии от обратных кавычек против
$(...)
в Баше, также избежать символов могут дать вам головные боль).Также обратите внимание, что приведенная выше форма допускает
(:regex "string")
формы, так что вы можете смешивать краткие обозначения с деревьями. Все это приводит ИМХО к хорошей читаемости и комбинируемости; он решает три проблемы, выраженные delnan , косвенно (т.е. не на языке самих регулярных выражений).Заключить
Для большинства целей краткая запись фактически читаема. Существуют трудности при работе с расширенными нотациями, которые включают возврат и т. Д., Но их использование редко оправдано. Необоснованное использование регулярных выражений может привести к нечитаемым выражениям.
Регулярные выражения не обязательно должны быть закодированы как строки. Если у вас есть библиотека или инструмент, который может помочь вам создавать и составлять регулярные выражения, вы избежите множества потенциальных ошибок, связанных со строковыми манипуляциями.
В качестве альтернативы формальные грамматики более читабельны и лучше именуют и абстрагируют подвыражения. Терминалы обычно выражаются в виде простых регулярных выражений.
1. Вы можете предпочесть создавать свои выражения во время чтения, потому что регулярные выражения имеют тенденцию быть константами в приложении. Смотрите
create-scanner
иload-time-value
:источник
digits
,ident
и сочинять их. Как я понимаю, они обычно используют манипуляции со строками (конкатенацию или интерполяцию), что приводит к другим проблемам, таким как правильное экранирование. Поиск случаев\\\\`
в пакетах emacs, например. Кстати, это еще хуже, потому что один и тот же управляющий символ используется как для специальных символов, таких как\n
и, так\"
и для синтаксиса регулярных выражений\(
. Примером хорошего синтаксиса не является лисписprintf
, где%d
не конфликтует с\d
.greedy-repetition
не являются интуитивно понятными и все еще должны быть изучены). Тем не менее, он жертвует удобством использования для экспертов, так как гораздо сложнее увидеть и понять всю модель.do {optional (many1 (letter) >> char ':'); choice (map string ["///","//","/",""]); many1 (oneOf "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."); optional (char ':' >> many1 digit); optional (char '/' >> many (noneOf "?#")); optional (char '?' >> many (noneOf "#")); optional (char '#' >> many (noneOf "\n")); eof}
. С помощью нескольких строк, как обозначение длинной строки,domainChars = ...
иsection start p = optional (char start >> many p)
это выглядит довольно просто.Самая большая проблема с регулярным выражением не в слишком кратком синтаксисе, а в том, что мы пытаемся выразить сложное определение в одном выражении вместо того, чтобы составлять его из меньших строительных блоков. Это похоже на программирование, когда вы никогда не используете переменные и функции и вместо этого встраиваете свой код в одну строку.
Сравните регулярное выражение с BNF . Его синтаксис не намного чище, чем регулярные выражения, но он используется по-другому. Вы начинаете с определения простых именованных символов и составляете их, пока не получите символ, описывающий весь шаблон, который вы хотите сопоставить.
Например, посмотрите на синтаксис URI в rfc3986 :
Вы можете написать почти то же самое, используя вариант синтаксиса регулярных выражений, который поддерживает встраивание именованных подвыражений.
Лично я считаю, что краткий регулярный выражения, подобный синтаксису, подходит для часто используемых функций, таких как классы символов, конкатенация, выбор или повторение, но для более сложных и более редких функций, таких как прогнозные подробные имена, предпочтительнее. Очень похоже на то, как мы используем операторы, такие как
+
или*
в обычном программировании, и переключаемся на именованные функции для более редких операций.источник
это? Есть причина, по которой большинство языков имеют {и} в качестве разделителей блоков, а не BEGIN и END.
Людям нравится краткость, и как только вы узнаете синтаксис, короткая терминология лучше. Представьте себе свой пример регулярного выражения, если бы d (для цифры) было «цифрой», регулярное выражение было бы еще более ужасным для чтения. Если бы вы сделали его более легко разбираемым с управляющими символами, то это было бы больше похоже на XML. Ни один не так хорош, как только вы знаете синтаксис.
Чтобы правильно ответить на ваш вопрос, вы должны понимать, что регулярное выражение исходит от тех времен, когда краткость была обязательной. Сегодня легко подумать, что XML-документ объемом 1 МБ не представляет особой проблемы, но мы говорим о тех днях, когда 1 МБ было достаточно вся ваша емкость. В то время также использовалось меньше языков, и регулярное выражение не было в миллионах миль от Perl или C, поэтому синтаксис был бы знаком программистам того времени, которые были бы рады изучению синтаксиса. Так что не было причин делать это более многословным.
источник
selfDocumentingMethodName
в целом согласились быть лучше ,e
потому что программист интуиция не совпадает с реальностью в плане того , что на самом деле представляет собой читаемость или хороший код качества . Люди, которые соглашаются, ошибаются, но это так.e()
это лучше, чемselfDocumentingMethodName()
?e()
самодокументированного имени метода . Можете ли вы объяснить, в каком контексте лучше использовать однобуквенные имена методов, а не описательные имена методов?Регекс похож на кусочки лего. На первый взгляд, вы видите несколько пластиковых деталей различной формы, которые можно соединить. Вы можете подумать, что не будет слишком много возможных разных вещей, которые вы можете придумать, но потом вы увидите удивительные вещи, которые делают другие люди, и вы просто удивляетесь, насколько это удивительная игрушка.
Регекс похож на кусочки лего. Есть несколько аргументов, которые можно использовать, но связывание их в разных формах приведет к образованию миллионов различных шаблонов регулярных выражений, которые можно использовать для многих сложных задач.
Люди редко использовали одни только параметры регулярного выражения. Многие языки предлагают вам функции для проверки длины строки или выделения ее числовых частей. Вы можете использовать строковые функции, чтобы разрезать тексты и преобразовать их. Сила регулярных выражений замечается, когда вы используете сложные формы для выполнения очень специфических сложных задач.
Вы можете найти десятки тысяч вопросов регулярных выражений в SO, и они редко помечаются как дубликаты. Уже одно это показывает возможные уникальные варианты использования, которые сильно отличаются друг от друга.
И нелегко предлагать заранее определенные методы для решения этих совершенно разных уникальных задач. У вас есть строковые функции для таких задач, но если этих функций недостаточно для вашей конкретной задачи, то пришло время использовать регулярные выражения
источник
Я понимаю, что это проблема практики, а не потенции. Проблема обычно возникает, когда регулярные выражения реализуются напрямую , а не предполагают составной характер. Точно так же хороший программист разлагает функции своей программы на лаконичные методы.
Например, строка регулярного выражения для URL может быть уменьшена примерно с:
чтобы:
Регулярные выражения - изящные вещи, но они склонны злоупотреблять теми, кто оказывается поглощенным их очевидной сложностью. Полученные выражения являются риторикой, отсутствуют долгосрочные значения.
источник
Как говорит @cmaster, регулярные выражения изначально были предназначены для использования только на лету, и это просто странно (и немного удручает), что синтаксис строкового шума по-прежнему остается самым популярным. Единственные объяснения, которые я могу придумать, включают инерцию, мазохизм или мачизм (не часто «инерция» является наиболее привлекательной причиной для того, чтобы что-то делать ...)
Perl делает довольно слабую попытку сделать их более читабельными, допуская пробелы и комментарии, но не делает ничего отдаленно воображаемого.
Есть и другие синтаксисы. Хорошим примером является синтаксис scsh для регулярных выражений , который, по моему опыту, дает регулярные выражения , которые достаточно легко набрать, но все же читаемые после факта.
[ scsh великолепен по другим причинам, только одна из которых - его знаменитый текст подтверждений ]
источник
Я считаю, что регулярные выражения были разработаны так, чтобы быть как можно более общими и простыми, поэтому их можно использовать (примерно) одинаково в любом месте.
Ваш пример
regex.isRange(..).followedBy(..)
связан как с синтаксисом конкретного языка программирования, так и, возможно, с объектно-ориентированным стилем (цепочка методов).Как, например, будет выглядеть именно это регулярное выражение в C? Код должен быть изменен.
Наиболее «общий» подход состоит в том, чтобы определить простой лаконичный язык, который затем можно легко встроить в любой другой язык без изменений. И это (почти) то, что регулярное выражение.
источник
Совместимые с Perl механизмы регулярных выражений широко используются, предоставляя краткий синтаксис регулярных выражений, который понимают многие редакторы и языки. Как отметил @ JDługosz в комментариях, Perl 6 (не просто новая версия Perl 5, но совершенно другой язык) попытался сделать регулярные выражения более читабельными, создав их из отдельных элементов. Например, вот пример грамматики для анализа URL-адресов из Wikibooks :
Разделение регулярного выражения, подобное этому, позволяет каждому биту быть индивидуально определенным (например, ограничивающим,
domain
чтобы быть буквенно-цифровым) или расширенным посредством подклассов (например,FileURL is URL
эти ограниченияprotocol
должны быть только"file"
).Итак: нет, технических причин для краткости регулярных выражений нет, но новые, более понятные и понятные способы их представления уже здесь! Надеюсь, мы увидим новые идеи в этой области.
источник