Чи впливає пошуковий запит на те, які мови можна порівняти за допомогою регулярних виразів?


78

Є деякі особливості сучасних механізмів регулярних виразів, які дозволяють узгоджувати мови, яким неможливо було б підійти без цієї функції. Наприклад наступне регулярний вираз , використовуючи зворотні посилання збігається з мовою всіх рядків , які складаються з слова , яке повторюється: (.+)\1. Ця мова не є звичайною і не може відповідати регулярному виразу, який не використовує зворотні посилання.

Чи впливає пошук також на те, з якими мовами може відповідати регулярний вираз? Тобто чи існують мови, з якими можна підібрати підстановку, яка не могла б відповідати інакше? Якщо так, чи це справедливо для всіх смаків підгляду (негативного чи позитивного погляду або позаду) чи лише для деяких з них?


4
regular-expressions.info/lookaround.html стверджує, що "Lookarounds дозволяють створювати регулярні вирази, які неможливо створити без них, або які б дуже довго накручувались без них". Але єдиний приклад в цьому напрямку про неможливість знайти і відповідати д годі було ц . Це нічого не говорить про те, чи можна сказати , якщо рядок введення містить в д не що супроводжується U (без зіставлення тільки , що д ).
Крістіан Семрау

7
@ChristianSemrau: Це може бути не програмування питання сам по собі, але вимога тільки «програмування пов'язаних з » , і я думаю , що це кваліфікується. І для мене це питання насправді цікаве з практичної точки зору, оскільки воно постало під час програмування.
sepp2k

2
@Christian Semrau: Мій головний критерій "пов'язаного з програмуванням" полягав би у тому, якби запитання було б вдома на подібному бухгалтерському сайті (з очевидними простими замінами). Regexes - це досить строго програмування. Я особисто вважаю це по темі.
Девід Торнлі,

5
Очевидно, питання про належність CS до stackoverflow вже обговорювалося раніше: meta.stackexchange.com/questions/26889/… . Особисто я сподіваюся побачити тут більше запитань щодо CS, або, можливо, довідковий сайт, якщо це необхідно.
полігенні мастила

Відповіді:


25

Як стверджують інші відповіді, пошукові запити не додають жодної додаткової сили регулярним виразам.

Я думаю, ми можемо показати це, використовуючи наступне:

Один Pebble 2-NFA (див. Розділ «Вступ», який посилається на нього).

1-гальковий 2NFA не стосується вкладених головок підстановки, але ми можемо використовувати варіант багатогалькових 2NFA (див. Розділ нижче).

Вступ

2-NFA - це недетермінований кінцевий автомат, який має можливість рухатись ліворуч або праворуч на його вході.

Машина з одним камінчиком - це місце, де машина може помістити камінчик на вхідну стрічку (тобто позначити певний камінний символ вхідним каменем) і, можливо, робити різні переходи залежно від того, чи є камінчик у поточній вхідній позиції чи ні.

Відомо, що One Pebble 2-NFA має таку ж потужність, як звичайний DFA.

Невкладені голови Lookaheads

Основна ідея така:

2NFA дозволяє нам відстежувати (або "передню доріжку"), рухаючись вперед або назад у вхідній стрічці. Отже, для lookahead ми можемо виконати збіг для регулярного виразу lookahead, а потім повернути назад те, що ми спожили, для відповідності виразу lookahead. Для того, щоб точно знати, коли зупинити зворотне відстеження, ми використовуємо гальку! Ми опускаємо камінчик, перш ніж увійти в dfa для голови, щоб позначити місце, де зворотне відстеження має зупинитися.

Таким чином, наприкінці проходження нашого рядка через гальку 2NFA ми знаємо, чи відповідали ми виразу lookahead чи ні, а вхідний лівий (тобто те, що залишилось спожити) - це саме те, що потрібно, щоб відповідати решті.

Отже, для вигляду виду u (? = V) w

У нас є DFA для u, v і w.

З приймаючого стану (так, можна припустити, що існує лише один) DFA для u, ми робимо електронний перехід у початковий стан v, позначаючи вхід камінчиком.

З приймаючого стану для v ми здійснюємо електронний перехід до стану, який продовжує рухати вхід вліво, поки не знайде камінчик, а потім переходить у початковий стан w.

Від відхиляючого стану v ми здійснюємо електронний перехід до стану, який рухається ліворуч, поки не знайде камінчик, і переходить у прийнятний стан u (тобто там, де ми зупинилися).

Доказ, який використовується для звичайних NFA, щоб показати r1 | r2, або r * і т. д., перенесіть для цього одного гальки 2nfas. Див http://www.coli.uni-saarland.de/projects/milca/courses/coal/html/node41.html#regularlanguages.sec.regexptofsa для отримання додаткової інформації про те , компонентні машини ставляться разом , щоб дати велику машину для виразу r * тощо

Причина, по якій вищезазначені докази для r * etc працюють, полягає в тому, що зворотне відстеження гарантує, що вказівник введення завжди знаходиться в потрібному місці, коли ми вводимо компонент nfas для повторення. Крім того, якщо використовується галька, вона обробляється однією з машин для пошуку деталей. Оскільки немає переходів від машини lookahead до машини lookahead без повного зворотного відстеження та повернення гальки, потрібна лише одна галька.

Наприклад, розглянемо ([^ a] | a (? = ... b)) *

і рядок abbb.

У нас є abbb, який проходить через peb2nfa для a (? = ... b), в кінці якого ми знаходимось у стані: (bbb, збіг) (тобто вхідний bbb залишається, і він відповідає "a" далі - "..b"). Тепер через * ми повертаємося до початку (див. Конструкцію за посиланням вище) і вводимо dfa для [^ a]. Зіставте b, поверніться до початку, двічі введіть [^ a], а потім прийміть.

Робота з вкладеними лукахедами

Для обробки вкладених головок пошуку ми можемо використовувати обмежену версію k-pebble 2NFA, як визначено тут: Результати складності для двосторонніх та багатогалькових автоматів та їх логіки (див. Визначення 4.1 та Теорему 4.2).

Загалом, 2 камінчики можуть приймати нерегулярні набори, але з наступними обмеженнями, k-камінчики можуть бути показані як регулярні (теорема 4.2 у цій статті).

Якщо галька P_1, P_2, ..., P_K

  • P_ {i + 1} не можна розміщувати, якщо P_i вже на стрічці, а P_ {i} не може бути піднятий, якщо P_ {i + 1} немає на стрічці. В основному камінчики потрібно використовувати LIFO.

  • Між часом, коли P_ {i + 1} розміщується, і часом, коли P_ {i} піднімається або P_ {i + 2} розміщується, автомат може обходити лише підслово, розташоване між поточним місцезнаходженням P_ {i} і кінець вхідного слова, який лежить у напрямку P_ {i + 1}. Більше того, у цьому підслові автомат може діяти лише як 1-гальковий автомат з Pebble P_ {i + 1}. Зокрема, забороняється піднімати, розміщувати або навіть відчувати присутність іншого гальки.

Отже, якщо v - вкладений вираз Lookahead глибини k, то (? = V) - вкладений вираз Lookahead глибини k + 1. Коли ми потрапляємо в машину для пошуку, ми точно знаємо, скільки гальки потрібно було покласти до цього часу, і тому можемо точно визначити, яку гальку розмістити, і коли ми виходимо з цієї машини, ми знаємо, яку гальку підняти. Усі машини на глибині t вводяться, розміщуючи гальку t, і виходять (тобто ми повертаємось до обробки машини глибини t-1), видаляючи гальку t. Будь-який запуск цілої машини виглядає як рекурсивний виклик dfs дерева, і на ці два обмеження багатогальцевої машини можна вжити.

Тепер, коли ви комбінуєте вирази, для rr1, оскільки ви конкатуєте, кількість гальки r1 повинна збільшуватися на глибину r. Для r * і r | r1 нумерація гальки залишається незмінною.

Таким чином, будь-який вираз із пошуковими головами можна перетворити на еквівалентну багатогалькову машину із зазначеними вище обмеженнями щодо розміщення гальки, і тому є регулярним.

Висновок

Це в основному усуває недолік оригінального доказу Френсіса: можливість запобігти тому, щоб вирази lookahead споживали все, що потрібно для майбутніх матчів.

Оскільки Lookbehinds - це просто скінченний рядок (насправді не регулярні вирази), ми можемо мати справу з ними спочатку, а потім мати справу з пошуковими головами.

Вибачте за неповний запис, але повний доказ передбачає малювання великої кількості фігур.

Мені це виглядає правильно, але я буду радий дізнатися про будь-які помилки (які, здається, мені подобаються :-)).


Я не впевнений, що це обробляє декілька запитань, наприклад u(?=v)(?=w)(?=x)z?
Френсіс Дейві

Коли ми виходимо з гальки 2NFA для пошуку голови, ми повертаємось до стану вхідної стрічки, куди ми увійшли, з галькою, яку ми використовуємо, і залежно від відповідності чи ні для точки пошуку, ми перебуваємо в одному з двох різних станів (тобто ми можемо скажіть, чи був сірник). Отже, здається, це буде працювати, просто об’єднавши автомати (з додатковими станами з електронними переходами, які ми додали), оскільки ми завжди отримуємо гальку. Але, мабуть, це залежить від того, як ви інтерпретуєте цей вислів. Це те саме, що u (? = Vwx) z? або ((u (? = v))? = w) ... тощо?

Вираз відповідає au, за яким слід (не споживаючи) усі три v, w та x (де v, w та x - це загальні регулярні вирази) та z. Спробувавши побудувати щось, що дозволить це вирішити, я впевнений, що ви не можете зробити це композиційно (тобто шляхом об'єднання рішень).
Френсіс Дейві

@Francis: Якщо це має збігатися з усіма, то об'єднання працює (я думаю). ми конкатуємо це як dfa (u) -> peb2ndfa (v) -> peb2ndfa (w) -> dfa (x). Якщо після зіставлення з u ми не збігаємося з v або w, ми повертаємось до u і продовжуємо там, де зупинилися. Якщо ми збігаємося з v, то, оскільки ми повертаємось назад після того, як v закінчено, ми можемо знов зіставити w (що знову повертається), а потім зрівняти x. Ключовим є те, що 2NDFA дозволяє нам відстежувати, а галька - знати, коли зупинити зворотне відстеження.

@ sepp2k: Чи отримували ви можливість прочитати цю відповідь? Якщо у вас є питання / роз’яснення / контрприклади, я був би радий відповісти.

27

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

Доказ є відносно простим, але алгоритм перекладу регулярного виразу, що містить пошукові окружності, в один без є брудним.

Перше: зверніть увагу, що ви завжди можете заперечити регулярний вираз (над кінцевим алфавітом). Враховуючи автомат скінченного стану, який розпізнає мову, що генерується виразом, ви можете просто обміняти всі приймаючі стани на неприйняті стани, щоб отримати FSA, який точно розпізнає заперечення цієї мови, для якої існує сімейство еквівалентних регулярних виразів .

По-друге: оскільки регулярні мови (і, отже, регулярні вирази) закриті за запереченням, вони також закриті під перетином, оскільки A перетинають B = neg (neg (A) союз neg (B)) за законами де Моргана. Іншими словами, враховуючи два регулярні вирази, ви можете знайти інший регулярний вираз, який відповідає обом.

Це дозволяє імітувати вирази підстановки. Наприклад, u (? = V) w відповідає лише виразам, які будуть відповідати uv та uw.

Для від’ємного пошуку потрібно регулярний вираз, еквівалентний теоретичному множині A \ B, який є просто перетином A (neg B) або еквівалентно neg (neg (A) об’єднання B). Таким чином, для будь-яких регулярних виразів r і s ви можете знайти регулярний вираз rs, який відповідає тим виразам, які відповідають r і не відповідають s. У негативному виразі lookahead: u (?! V) w відповідає лише тим виразам, які відповідають uw - uv.

Є дві причини, чому пошук є корисним.

По-перше, тому що заперечення регулярного виразу може призвести до чогось набагато менш охайного. Наприклад q(?!u)=q($|[^u]).

По-друге, регулярні вирази мають більше, ніж відповідні вирази, вони також споживають символи з рядка - або принаймні так ми любимо про них думати. Наприклад, у python я дбаю про .start () та .end (), отже, звичайно:

>>> re.search('q($|[^u])', 'Iraq!').end()
5
>>> re.search('q(?!u)', 'Iraq!').end()
4

По-третє, і я думаю, що це досить важлива причина, заперечення регулярних виразів погано піднімається над об'єднанням. neg (a) neg (b) - це не те саме, що neg (ab), що означає, що ви не можете перекласти підстановку з контексту, в якому ви її знайдете - вам доведеться обробити весь рядок. Я думаю, це робить людей неприємними для роботи та порушує інтуїцію людей щодо регулярних виразів.

Сподіваюсь, я відповів на ваше теоретичне запитання (це пізно ввечері, тому пробачте мене, якщо я незрозумілий). Я погоджуюсь з коментатором, який сказав, що це має практичне застосування. З такою ж проблемою я зіткнувся, намагаючись скребти деякі дуже складні веб-сторінки.

РЕДАГУВАТИ

Приношу свої вибачення за незрозумілість: я не вірю, що ви можете довести регулярність регулярних виразів + пошукових обведень за допомогою структурної індукції, мій приклад u (?! V) w мав бути саме цим, прикладом і простим при цьому. Причина, по якій структурна індукція не спрацює, полягає в тому, що пошукові круги поводяться некомпозиційно - те, що я намагався сказати щодо негацій вище. Я підозрюю, що в будь-якому прямому офіційному доказі буде багато брудної деталі. Я намагався придумати простий спосіб показати це, але не можу придумати такого з маківки.

Для ілюстрації, використовуючи перший приклад Джоша, ^([^a]|(?=..b))*$це еквівалентно DFSA із 7 станами, усі стани приймають:

A - (a) -> B - (a) -> C --- (a) --------> D 
Λ          |           \                  |
|          (not a)       \               (b)
|          |              \               | 
|          v                \             v
(b)        E - (a) -> F      \-(not(a)--> G  
|            <- (b) - /                   |
|          |                              |
|         (not a)                         |
|          |                              |
|          v                              |
\--------- H <-------------------(b)-----/

Регулярний вираз для стану A виглядає так:

^(a([^a](ab)*[^a]|a(ab|[^a])*b)b)*$

Іншими словами, будь-який регулярний вираз, який ви збираєтесь отримати, усуваючи перегляди, загалом буде набагато довшим і набагато меншим.

Відповісти на коментар Джоша - так, я думаю, що найбільш прямий спосіб довести еквівалентність - це FSA. Що робить цей месіє тим, що звичайний спосіб побудови FSA здійснюється за допомогою недетермінованої машини - набагато простіше виразити u | v простою машиною, побудованою з машин для u та v з епсилоновим переходом до них двох. Звичайно, це еквівалентно детермінованій машині, але з ризиком експоненціального вибуху станів. Тоді як заперечення набагато простіше зробити за допомогою детермінованої машини.

Загальний доказ включатиме взяття декартового добутку двох машин та вибір тих станів, які ви хочете зберегти в кожній точці, в яку ви хочете вставити пошук. Наведений вище приклад ілюструє те, що я певною мірою маю на увазі.

Прошу вибачення за те, що не поставив будівлю.

ДОДАТКОВЕ РЕДАГУВАННЯ: Я знайшов допис у блозі, де описується алгоритм генерування DFA із регулярного виразу, доповненого пошуковими кругами. Це акуратно, оскільки автор очевидно розширює ідею NFA-e із "позначеними переходами епсилону", а потім пояснює, як перетворити такий автомат на DFA.

Я думав, що щось подібне буде способом зробити це, але мені приємно, що хтось це записав. Мені було неможливо придумати щось таке акуратне.


2
Я погоджуюсь з Френсісом, що перегляди є регулярними, але я вважаю, що доказ невірний. Проблема полягає в тому, що ви не можете розбити регулярний вираз з переглядом на два регулярні вирази A і B загалом. Френсіс зробив це, перетворившись u(?!v)wна uwі uv, але я не вірю, що існує алгоритм для цього взагалі. Натомість ви можете приєднати lookahead або neg (lookahead) до вихідного DFA в точці, де це відбувається з переходом епсилону. Деталі цього трохи хитрі, але я думаю, що це працює.
Джош Хаберман,

1
Наприклад, розглянемо регулярний вираз ^([^a]|a(?=..b))*$. Іншими словами, дозволяються всі символи, але за кожним символом "а" через три символи слід піти "b". Я не вірю, що ви можете звести це до двох регулярних виразів A і B, які ви поєднуєте за допомогою об'єднання. Я думаю, що ви повинні зробити позитивну перспективу в частині побудови NFA.
Джош Хаберман,

1
@Josh, sepp2k: Для кожної регулярної мови L існує еквівалентний регулярний вираз і навпаки. Тепер a (? = .. b) є регулярним, він відповідає якомусь виразу сказати r. Тепер у вас є ([^ a] | r) *, що знову є регулярним. Я вважаю, що це було доведено Кліном. Перевірте це: coli.uni-saarland.de/projects/milca/courses/coal/html/… . Доказ за допомогою індукції справді працює. Те, що вам здається завислим, є фундаментальним фактом щодо регулярних виразів та мов (моє перше речення в цьому коментарі).

3
@Moron: ви припускаєте, що вирази lookahead складаються так само, як і регулярні вирази. Ви припускаєте, що мова ([^a]|r)*відповідає тій самій мові ([^a]|a(?=..b)), що не відповідає дійсності , навіть якщо вона rвідповідає тій самій мові, що і a(?=..b). Якщо ви самі зробите розширення DFA, ви побачите. Оскільки lookahead відповідає символам, не споживаючи їх, він не складається так само, як регулярні вирази. Якщо ви все ще не впевнені в цьому, я опублікую фактичне розширення DFA пізніше.
Джош Хаберман,

2
Як короткий доказ цього, розглянемо, що a(?=..b)це порожня мова, тому що a ∩ a..b = ϵ. Отже, якщо ми дотримуємося Вашої аргументації r = ϵта ([^a]|a(?=..b))*еквівалентна ([^a]|ϵ)*чи справедлива [^a]*. Але це явно хибно, оскільки aaabвідповідає вихідному регулярному виразу, але не імовірно еквівалентному.
Джош Хаберман,

10

Я погоджуюсь з іншими повідомленнями, що перегляд є регулярним (мається на увазі, що він не додає жодних фундаментальних можливостей до регулярних виразів), але я маю для цього аргумент, який простіший IMO, ніж інші, які я бачив.

Я покажу, що пошук є регулярним, надаючи конструкцію DFA. Мова є звичайною тоді і лише тоді, коли вона має DFA, який її розпізнає. Зверніть увагу, що Perl насправді не використовує DFA внутрішньо (див. Цей документ для деталей: http://swtch.com/~rsc/regexp/regexp1.html ), але ми створюємо DFA для цілей доказу.

Традиційним способом побудови DFA для регулярного виразу є спочатку побудова NFA за допомогою алгоритму Томпсона. Дано два регулярних вирази фрагментів r1і r2, алгоритм Томпсона забезпечує конструкції для конкатенації ( r1r2), чергування ( r1|r2) і повторення ( r1*) регулярних виразів. Це дозволяє створювати NFA побітно, що розпізнає вихідний регулярний вираз. Докладніше див. У статті вище.

Щоб показати, що позитивні та негативні результати пошуку регулярні, я запропоную конструкцію для конкатенації регулярного виразу uз позитивним чи негативним результатом пошуку: (?=v)або (?!v). Лише конкатенація вимагає особливого ставлення; звичайні конструкції чергування та повторення працюють нормально.

Побудова призначена як для u (? = V), так і для u (?! V):

http://imgur.com/ClQpz.png

Іншими словами, підключіть кожен кінцевий стан існуючого NFA для uяк до стану прийняття, так і до NFA для v, але модифікованого наступним чином. Функція f(v)визначається як:

  • Нехай aa(v)буде функцією на NFA, vяка змінює кожен стан прийняття на "стан антиприйняття". Стан антиприйняття визначається як стан, який призводить до збою збігу, якщо будь-який шлях через NFA закінчується в цьому стані для даного рядка s, навіть якщо інший шлях до vfor sзакінчується в стані прийняття.
  • Нехай loop(v)буде функцією на NFA, vяка додає самоперехід у будь-якому стані прийняття. Іншими словами, як тільки шлях веде до стану прийняття, цей шлях може назавжди залишатися в стані прийняття, незалежно від того, який вхід слід.
  • Для негативного перегляду, f(v) = aa(loop(v)).
  • Для позитивного випереджаючого перегляду, f(v) = aa(neg(v)).

Щоб надати інтуїтивний приклад, чому це працює, я буду використовувати регулярний вираз (b|a(?:.b))+, який є дещо спрощеною версією регулярного виразу, який я запропонував у коментарях доказів Френсіса. Якщо ми використовуємо мою конструкцію разом із традиційними конструкціями Томпсона, у нас вийде:

текст заміщення

В es є епсилон переходів (переходи , які можуть бути прийняті без споживання якого - або входу) і анти-приймати стан позначено X. У лівій половині графіку ви бачите подання (a|b)+: any aабо bпереводить графік у стан прийняття, але також дозволяє перехід назад у початковий стан, щоб ми могли зробити це знову. Але зауважте, що кожного разу, коли ми збігаємось з, aми також вводимо праву половину графіку, де ми знаходимося в станах, що не приймають, доки ми не зрівняємо "будь-який", за яким слідує a b.

Це не традиційний НФА, оскільки традиційні НФА не мають антиприйнятих станів. Однак ми можемо використовувати традиційний алгоритм NFA-> DFA, щоб перетворити його на традиційний DFA. Алгоритм працює як зазвичай, де ми імітуємо кілька запусків NFA, роблячи так, щоб наші стани DFA відповідали підмножинам станів NFA, в яких ми могли б знаходитися. Єдиним поворотом є те, що ми трохи посилюємо правило для прийняття рішення про те, чи стан DFA є приймати (остаточний) стан чи ні. У традиційному алгоритмі стан DFA є станом прийняття, якщо будь-який із станів NFA був станом прийняття. Ми модифікуємо це, щоб сказати, що стан DFA є станом прийняття тоді і тільки тоді, коли:

  • = 1 стан NFA - це стан прийняття, і

  • 0 Штати NFA є державами, що не приймають.

Цей алгоритм надасть нам DFA, який розпізнає регулярний вираз із пошуковим головою. Ерго, пошуки регулярні. Зверніть увагу, що для погляду позаду потрібні окремі докази.


У машині, яку ви дали, ви приймаєте a. Якого немає в (b | a (? =. B)). Також анти-акцептний стан - це стан акцепту, коли збіг не вдається? Тоді за визначенням держави прийняття не існує держав, що приймають анти-акцепт! Або мені чогось не вистачає?

@ Морон: Я думаю, вам не вистачає сенсу моїх антиприйнятих станів. Ось та сама діаграма, але з нумерованими станами: imgur.com/ho4C8.png Мій апарат не приймає a, оскільки після встановлення відповідності aми можемо перейти до станів 4, 3, 1 та 5 (використовуючи алгоритм NFA-> DFA). Але стан 5 є антиприйнятним станом, тому, дотримуючись правил внизу мого запису, стан DFA, що відповідає станам 4, 3, 1 і 5, не є станом прийняття.
Джош Хаберман,

@Josh: Чи не є визначення aa(v)залежності від рядка s? тобто набір aa(v)може змінюватися залежно від s. Також ви кажете, що держава, що приймає, починається з того, що вона приймає. Тоді як будь-який сірник може провалитися, якщо машина опиниться в такому стані? Вибачте, якщо я читаю це неправильно.

@Moron: aa(v)просто перевертає всі держави прийняття натомість як антиприймаючі , тому це не повинно залежати від s. Обидва vі aa(v)є NFA, а не набори. Я не слідую за вашим останнім коментарем: це правда, що в vдеяких країнах є стани прийняття, але aa(v)він не має станів прийняття, і aa(v)це те, що насправді опиняється в остаточному NFA.
Джош Хаберман,

@Josh: Ваше визначення: «Нехай аа (v) функція на NFA V , який змінює кожен приймає стан в анти-приймає стан ..». Таким чином, ви змінюєте стан прийняття P на стан неприйняття, якщо машина v закінчується станом P при якомусь запуску, і збіг не вдається. Але за визначенням (примітка v все ще є NFA) прийнятого стану, якщо машина на цьому закінчується, збіг пройшов! І під набором я мав на увазі набір станів у v, вам потрібно змінити на антиприйняття, зробити v в aa (v). Чи це не залежатиме від рядка s? Сподіваюся, я тепер зрозуміліший.

2

У мене відчуття, що тут задають два окремі запитання:

  • Чи є двигуни Regex, які містять "огляд", потужнішими, ніж двигуни Regex, які цього не роблять?
  • Чи надає "lookaround" механізм Regex можливості аналізувати мови, які є більш складними, ніж ті, що генеруються з граматики Хомського типу 3 - регулярні ?

Відповідь на перше запитання в практичному сенсі - так. Lookaround надасть двигуну Regex, який використовує цю функцію принципово більше енергії, ніж той, який цього не робить. Це пояснюється тим, що він забезпечує більш багатий набір "якорів" для процесу узгодження. Lookaround дозволяє визначити цілий Regex як можливу опорну точку (твердження нульової ширини). Ви можете отримати досить хороший огляд потужності цієї функції тут .

Lookaround, хоча і потужний, але не піднімає двигун Regex за теоретичні межі, встановлені на ньому граматикою типу 3. Наприклад, ви ніколи не зможете надійно проаналізувати мову на основі граматики Context Free - Type 2, використовуючи движок Regex, обладнаний пошуковим кругообігом. Двигуни Regex обмежені потужністю автоматизованої системи скінченного стану, і це принципово обмежує виразність будь-якої мови, яку вони можуть проаналізувати, до рівня граматики типу 3. Незалежно від того, скільки "хитрощів" додано до вашого механізму регулярних виразів, мов, що генеруються за допомогою Безконтактної граматики завжди залишатиметься за межами своїх можливостей. Безконтактний синтаксичний аналіз - граматика типу 2 вимагає автоматичної автоматичної розгортання, щоб "запам'ятати", де вона знаходиться в рекурсивній конструкції мови. Все, що вимагає рекурсивної оцінки граматичних правил, не може бути проаналізовано за допомогою движків Regex.

Підсумовуючи: Lookaround надає деякі практичні переваги двигунам Regex, але не "змінює гру" на теоретичному рівні.

РЕДАГУВАТИ

Чи існує якась граматика зі складністю десь між Типом 3 (Звичайний) та Типом 2 (Без контексту)?

Я вважаю, що відповідь - ні. Причина в тому, що немає теоретичного обмеження на розмір NFA / DFA, необхідного для опису звичайної мови. Він може стати довільно великим і, отже, недоцільним у використанні (або уточненні). Тут корисні ухилення, такі як "lookaround". Вони забезпечують короткий механізм, щоб вказати, що в іншому випадку призвело б до дуже великих / складних специфікацій NFA / DFA. Вони не підвищують виразність звичайних мов, вони просто роблять уточнення їх більш практичним. Як тільки ви зрозумієте цю думку, стає зрозумілим, що існує багато "функцій", які можна додати до движків Regex, щоб зробити їх більш корисними на практиці - але ніщо не зробить їх здатними виходити за межі звичайної мови .

Основна різниця між мовою Regular та Context Free полягає в тому, що мова Regular не містить рекурсивних елементів. Для того, щоб оцінити рекурсивну мову, вам потрібна автоматична автоматична система, щоб "запам'ятати", де ви знаходитесь в рекурсії. NFA / DFA не стекує інформацію про стан, тому не може обробити рекурсію. Отже, враховуючи нерекурсивне визначення мови, для опису буде вказано деякий NFA / DFA (але не обов’язково практичний вираз регулярних виразів).


1
Чи обов’язково правда, що граматика, потужніша за звичайну, повинна бути такою ж потужною, як і контекстова? тобто чи відомо, що немає граматики "між" двома?
BlueRaja - Danny Pflughoeft

@BlueRaja: Саме те, про що я думав: "гіпотеза про континуум граматики" :-)

@Moron @BlueRaja - я відредагував для вас свою відповідь. Сподіваюся, це допоможе.
NealB

4
Звичайно, існує багато класів граматик, що суворо між класом регулярних граматик та класом безконтекстних граматик, включаючи тривіальні приклади, такі як клас регулярних граматик разом із граматиками мови збалансованих дужок. У детермінованих контекстно - вільної граматики є більш корисним прикладом.
Крістіан Семрау,

An NFA/DFA does not stack state information so cannot handle the recursion.Так. Тож БУДЬ ЛАСКИ, ПРИПИНІТЬ СПРОБУВАТИ РОБИТИ РОЗБІР HTML з РЕГУЛЬНИМИ ВИРАЗАМИ!
Кранчер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.