Conversation
| 4. Поддержка захватывающих (possessive) квантификаторов и атомарных группировок. | ||
| 5. НКА поддерживает обработку многоопределённости. | ||
| В то же время, ДКА имеет полиномиальное время выполнения, в отличие от экспоненциального для НКА. | ||
| Комбинирование ДКА и НКА позволяет достигать необходимого баланса между скоростью работы и занимаемым в памяти местом (как известно, НКА с n состояниями может соответствовать ДКА с 2^n состояниями, а НКА имеет в худшем случае экспоненциальное время выполнения). При этом большое количество состояний и объём занимаемой ими памяти также могут негативно влиять на общую производительность (в случае непопадания в кэш процессорному ядру приходится обращаться в оперативную память, что увеличивает задержки). |
There was a problem hiding this comment.
в случае непопадания в кэш процессорному ядру приходится обращаться в оперативную память, что увеличивает задержки
подушню, но L3 кеш например пошарен между ядрами, но не думаю что тут оч сильно это важно
но формулировка ядро идет в оперативку мне как-то не оч нравится если честно
| questions: | ||
| - 'Что такое "сильная" LL(k) грамматика и чем она отличается от обычной? | ||
| ' | ||
| - answer: 'Обычно переписывание регулярных выражений для их ускорения осуществляется вручную, поскольку общего алгоритма для их оптимизации пока не существует, а доступные способы оптимизации зависят от сценария использования регуляного выражения (например, a{min, max} быстрее (a+)+, но для такой замены необходимо знать, сколько повторяющихся символов может быть во входной строке). Однако есть библиотеки и приложения, позволяющие оптимизировать регулярки хотя бы частично. Основной метод оптимизации - так называемая факторизация альтернаций суффиксов и префиксов, при которой альтернации максимально сокращаются, чтобы минимизировать обратные переходы (англ. backtracking). Например, `cat|car|calf` заменяется на `ca(?:t|r|lf)`, а `foo|goo` - на `[fg]oo`. Такой оптимизатор можно найти в языке Perl (модуль Regexp::Optimizer). |
There was a problem hiding this comment.
где-то тут кажется сломан формат yaml
There was a problem hiding this comment.
Регулярка (a+)+ в принципе ужасна )) Почему для её оптимизации нельзя просто заменить её хотя бы на a*(a+) (если вдруг нужна группа захвата) или тупо на a+, если группа захвата не нужна, а понадобились ограничители, мне не очень понятно.
There was a problem hiding this comment.
Регулярка (a+)+ в принципе ужасна )) Почему для её оптимизации нельзя просто заменить её хотя бы на a*(a+) (если вдруг нужна группа захвата) или тупо на a+, если группа захвата не нужна, а понадобились ограничители, мне не очень понятно.
язык (C+)+
))
| Современные устройства позволяют обрабатывать данные в несколько потоков (если учитывать возможность вычислений на GPU, то доступны сотни потоков). | ||
| Существует два основных подхода к параллелизации регулярных выражений: | ||
| 1. Построение параллельного конечного автомата (англ. parallel finite-state machine, PFSM) для регулярного выражения. | ||
| 2. Разбиение данных на несколько частей, каждая из которых обрабатывается движком регулярных выражений в отдельном потоке. Например, в Java для управлении потоками используется библиотека `java.util.concurrent`, в C++ - `std::thread`, а в Go - `goroutines`. |
There was a problem hiding this comment.
TLDR
ну строго говоря горутина != поток) и надо чуть пошаманить, чтобы горутины были на разных логических ядрах
еще подушню, но горутины и std::thread это разное с точки зрения того, что это под капотом. std:thread является потоком ОС, а вот горутина им не является, горутины шедулятся рантаймом Go, а потоки ОС планировщиком ОС
и тд)
можно долго продолжать, но прям сильно душить уж не буду
скажу только то, что горутина это абстракция над потоком ОС (будет интересно - прочитай про Green threads)
| author: Тверитнев Михаил | ||
| id: 266 | ||
| questions: | ||
| - 'Обратные ссылки с заданной глубиной в интерпретаторе регулярных выражений языка Ruby. |
| - 'Простой способ построения пересечения регулярных языков с помощью lookahead | ||
| ' | ||
| - answer: 'Алгоритм Бржозовского (Brzozowski algorithm) позволяет минимизировать любой конечный автомат, даже недетерминированный, посредством последовательного двукратного применения к нему операций обращения и детерминизации. | ||
| Теорема Бржозовского (англ. Brzozowski theorem, 1962): Пусть A - конечный автомат, распознающий язык L, det(A) - детерминизированный автомат, rev(A) - обратный автомат для А (т.е. такой автомат, в котором начальные и конечные состояния поменяли местами, а направления всех переходов изменены на обратные). Минимальный ДКА для автомата А может быть построен следующим образом: Amin = det(rev(det(rev(A)))). |
There was a problem hiding this comment.
1962 нам если честно не очень важно
| S -> aAa | ||
| S -> bA2ba | ||
| A -> b | e | ||
| A2 -> b | e. |
There was a problem hiding this comment.
давай A_2 заменим условно на C ) не вижу смысла юзать нижний регистр там, где можно обойтись без него
TonitaN
left a comment
There was a problem hiding this comment.
Материал интересный и нетривиальный, хотя иногда очень специфический, но понравилось.
| 5. НКА поддерживает обработку многоопределённости. | ||
| В то же время, ДКА имеет полиномиальное время выполнения, в отличие от экспоненциального для НКА. | ||
| Комбинирование ДКА и НКА позволяет достигать необходимого баланса между скоростью работы и занимаемым в памяти местом (как известно, НКА с n состояниями может соответствовать ДКА с 2^n состояниями, а НКА имеет в худшем случае экспоненциальное время выполнения). При этом большое количество состояний и объём занимаемой ими памяти также могут негативно влиять на общую производительность (в случае непопадания в кэш процессорному ядру приходится обращаться в оперативную память, что увеличивает задержки). | ||
| Что касается реализаций regex-машин в языках программирования, большинство современных regex-машин (PHP, PCRE2, Python re) используют НКА для поддержки своего расширенного синтаксиса. Regex-движки, реализующие ДКА, также используются в наше время - например, в реализации регулярок в Microsoft SQL Server (использование: <string_name> LIKE <regex>). Существуют и гибридные реализации, примерами которых являются re2 в Golang и библиотека Hyperscan от Intel. |
There was a problem hiding this comment.
Вот тут важно отметить, что перечисленные вами НКА машины вовсе не регулярные языки описывают. Они используют не НКА, а Extended NFA - расширенные НКА, у которых от НКА только каркас, а операции на ребрах могут быть и нерегулярными.
И re2 всё-таки не гошная библиотека изначально, а плюсовая. Другой вопрос, что гошные стандартные regexp имеют re2-совместимый синтаксис, и собственно re2, насколько помню, имеет гошный API. Поэтому я бы разделила здесь гошные regexp (тоже гибридные) и re2 выражения.
| questions: | ||
| - 'Какие regex-машины используют в своей реализации и ДКА, и НКА? | ||
| ' | ||
| - answer: 'Сильная LL(k)-грамматика (англ. strong LL(k) grammar) -- это LL(k)-грамматика, у которой, если в грамматике существуют правила A -> α, B -> β для всех нетерминалов множества FIRSTk(α FOLLOWk(A)) и FIRSTk(β FOLLOWk(A)) не имеют пересечений. На практике это означает, что решение разбора принимается только на основе следующих k токенов (символов). Это и есть определение LL(k)-грамматики, что позволяет сделать вывод о том, что любой LL(k) язык возможно описать LL(k)-сильной грамматикой. Однако не все LL(k)-грамматики являются сильными. Пример грамматики, не являющейся LL(k)-сильной (k = 2): |
There was a problem hiding this comment.
A и B никак не связаны, и B не используется нигде, кроме как в правиле. Поэтому сложно понять, что это за условие.
| questions: | ||
| - 'Что такое "сильная" LL(k) грамматика и чем она отличается от обычной? | ||
| ' | ||
| - answer: 'Обычно переписывание регулярных выражений для их ускорения осуществляется вручную, поскольку общего алгоритма для их оптимизации пока не существует, а доступные способы оптимизации зависят от сценария использования регуляного выражения (например, a{min, max} быстрее (a+)+, но для такой замены необходимо знать, сколько повторяющихся символов может быть во входной строке). Однако есть библиотеки и приложения, позволяющие оптимизировать регулярки хотя бы частично. Основной метод оптимизации - так называемая факторизация альтернаций суффиксов и префиксов, при которой альтернации максимально сокращаются, чтобы минимизировать обратные переходы (англ. backtracking). Например, `cat|car|calf` заменяется на `ca(?:t|r|lf)`, а `foo|goo` - на `[fg]oo`. Такой оптимизатор можно найти в языке Perl (модуль Regexp::Optimizer). |
There was a problem hiding this comment.
Регулярка (a+)+ в принципе ужасна )) Почему для её оптимизации нельзя просто заменить её хотя бы на a*(a+) (если вдруг нужна группа захвата) или тупо на a+, если группа захвата не нужна, а понадобились ограничители, мне не очень понятно.
| questions: | ||
| - 'Существуют ли инструменты для автоматической оптимизации регулярных выражений и как они устроены? | ||
| ' | ||
| - answer: 'Regex-движок Ruby Onigmo обладает уникальной особенностью: он позволяет использовать обратные ссылки с обозначением относительной глубины рекурсии, на которой должна быть ссылаемая группа. Например, выражение `\b(?word(?letter[a-z])\gword\kletter+0|[a-z])\b` распознаёт только палиндромы нечётной длины (например, слова `radar`, `racecar`, но не `abba` или `aa`). Если модифицировать данный пример, адресуя группу на предыдущем уровне рекурсии (`\b(?word(?letter[a-z])\gword(?:\kletter-1|z)|[a-z])\b`), можно распознавать такие слова, как `radrb`, `racearz`. Назовём их `сжатыми палиндромами`, где правая половина сдвинута к первой с потерей левых символов. Если же применить обратную ссылку с адресацией на следующем уровне рекурсии (`\b(?word(?letter[a-z])\gword(?:\kletter+1|z)|[a-z])\b`), мы получим выражение, распознающее слова вида `radzr`, `racezca`. Их можно назвать `растянутыми палиндромами`, где правая половина сдвигается вправо с потерей правых символов. |
There was a problem hiding this comment.
Вот эти выражения прямо пошагово расшифровать надо. В частности, разницу между ссылками \gName и \kName.
PS - Нигде кроме Oniguruma таких изощренностей, как сдвиг рекурсии, не видела. Не очень понимаю, как это осознанно использовать, но факт забавный.
| - 'Обратные ссылки с заданной глубиной в интерпретаторе регулярных выражений языка Ruby. | ||
| ' | ||
| - answer: 'В общем случае получение регулярного выражения, распознающего пересечение двух регулярных языков, довольно сложно. Для этого нужно представить каждый регулярный язык в виде ДКА, построить их прямое произведение и затем преобразовать результат в регулярное выражение. | ||
| Однако в современных regex-машинах, таких, как PCRE2 в PHP или V8 в JavaScript, есть возможность использовать опережающие проверки (lookahead) для получения пересечения. Например, если есть regex1 и regex2 для языков L1 и L2 соответственно, то PCRE2-совместимое регулярное выражение, распознающее пересечение этих языков, можно построить следующим образом: `^(?=regex1$)(?=regex2$)`. Следует помнить, что такой подход обладает своими недостатками: |
There was a problem hiding this comment.
И по итогу оно будет вычислять только одну подходящую подстроку. Т.к. после lookahead ещё нужно добавить основную часть, если мы пытаемся всё выражение распарсить как пересечение.
Тут кмк проще обойтись одной опережающей проверкой, а вторую регулярку положить как раз в базу.
| ' | ||
| Современные устройства позволяют обрабатывать данные в несколько потоков (если учитывать возможность вычислений на GPU, то доступны сотни потоков). | ||
| Существует два основных подхода к параллелизации регулярных выражений: | ||
| 1. Построение параллельного конечного автомата (англ. parallel finite-state machine, PFSM) для регулярного выражения. |
There was a problem hiding this comment.
На этот случай в регулярки вводится замечательная операция асинхронной композиции или шаффла, #. Её замечательность - в том, что она коммутативна, и алгебра регулярок с такой операцией позволяет определить производную произведения (асинхронной композиции) так же, как она определяется в классическом матанализе. Рекомендую в этом ответе добавить определение шаффла, раз уж пошла такая история.
data/data.yaml
Outdated
| ' | ||
| - answer: 'В PCRE2-совместимых регулярных выражениях (полное название стандарта - Perl Compatible Regular Expressions) предусмотрено использование нескольких способов обращения к скобочным группам, или группам захвата (англ. captive group): | ||
| 1. Прямое обращение по номеру. Группы в регулярках нумеруются слева направо, начиная с 1. | ||
| 2. Именованные группы. Для задания имени группе захвата используется следующий синтаксис: `(?<name>)`, где <name> - имя группы. Примеры: |
| Пример: `^(?<Counter>A)+ (?<-Counter>B)+ (?(Counter)(?!))$` распознаёт слова из языка L = {A^nB^n | n ∈ N, n > 0 }. | ||
| Более того, со стека можно снимать несколько захваченных символов. Например, регулярное выражение `^(?=.*[a-z](?<N>)|)(?=.*[A-Z](?<N>)|)(?=.*[0-9](?<N>)|)(?<-N>){2}.{8,}` распознаёт слова (пароли), состоящие не менее чем из восьми символов и содержащие не менее двух из трёх видов символов: строчные буквы, прописные буквы, цифры. | ||
| ' | ||
| author: Тверитнев Михаил |
|
На всякий случай, чтобы не было "никаких разночтений (с)" - когда я предлагаю правки, их совсем не обязательно и обычно не желательно вставлять в ответ методом copy&paste (исключение - если речь о точных формулировках). Всегда лучше подстраивать предложение под контекст. Да и некрасиво, если стилистика ответа начинает резко меняться, потому что часть писал студент, а часть - получается что я. Не к тому, что вы так делали, а к тому, что так делали некоторые другие, и на этот образец равняться не надо. Тегну здесь ещё @ark2016 с аналогичным пожеланием, чтобы вам не казалось, что про вас забыли) |
1) Добавлен обрезанный вопрос с id=274; 2) Добавлены примеры для вопроса с id=273
|
@ktveritnev23 , по всем правкам нужно пройтись, и не в последнюю ночь перед комиссией |
Вопросы по возможностям современных regex-машин в современных ЯП и конечным автоматам.