Путаница с атомной группировкой - чем она отличается от группировки в регулярном выражении Ruby?

Я только что просмотрел документы для Атомная Группировка и rubyinfo и несколько быстрых вопросов пришли мне на ум следующим образом:

  1. почему название пришло как "атомная группировка"? Что?!--9-->"атомарность" там есть общие группировка нет.
  2. как атомная группировка отличается от общие группировка?
  3. почему атомные группы называют незахватывающие группы?

Я попытался понять приведенный ниже код, но имел путаницу в выводе и как по-разному они работают на той же строке?

irb(main):001:0> /a(?>bc|b)c/ =~ "abbcdabcc"
=> 5
irb(main):004:0> $~
=> #<MatchData "abcc">
irb(main):005:0> /a(bc|b)c/ =~ "abcdabcc"
=> 0
irb(main):006:0> $~
=> #<MatchData "abc" 1:"b">

3 ответов


A () имеет некоторые свойства (включая такие, как (?!pattern), (?=pattern), etc. а равнина! .. --5-->), но общим свойством между ними является группировка, что делает произвольный шаблон одной единицей (единица-моя собственная терминология), что полезно при повторении.

обычный захват (pattern) обладает свойством захват и группа. Захват означает, что текст соответствует шаблону внутри будет захвачен так, что вы сможете использовать его с назад-ссылкой, в соответствовать или замене. Группа без захвата (?:pattern) не имеет свойства захвата, поэтому он сэкономит немного места и немного ускорится по сравнению с (pattern) так как он не хранит начальный и конечный индекс строки, соответствующей шаблону внутри.

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

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

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

пример

входной строки: bbabbbabbbbc
Выкройка:/(?>.*)c/

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


входной строки: bbabbbabbbbc
Выкройка:/((?>.*)|b*)[ac]/, для тестирования /(((?>.*))|(b*))[ac]/

есть 3 совпадения с этим регулярным выражением, которые являются bba, bbba, bbbbc. Если вы используете 2-е регулярное выражение, которое то же самое, но с захватом групп, добавленных для отладки, вы можете видеть, что все совпадения являются результатом сопоставления b* внутри.

вы можете увидеть поведение backtracking здесь.

  • без атомной группировки /(.*|b*)[ac]/, строка будет иметь одно совпадение, которое является всей строкой, из-за отступления в конце, чтобы соответствовать [ac]. Обратите внимание, что двигатель вернется к .* вернуться на 1 символ, так как у него все еще есть другие возможности.

    Pattern: /(.*|b*)[ac]/
    bbabbbabbbbc
    ^             -- Start matching. Look at first item in alternation: .*
    bbabbbabbbbc
                ^ -- First match of .*, due to greedy quantifier
    bbabbbabbbbc
                X -- [ac] cannot match
                  -- Backtrack to ()      
    bbabbbabbbbc
               ^  -- Continue explore other possibility with .*
                  -- Step back 1 character
    bbabbbabbbbc
                ^ -- [ac] matches, end of regex, a match is found
    
  • С атомной группировки, все возможности .* отрезается и ограничивается первым совпадением. Поэтому после жадного поедания целого строка и не совпадают, двигатель должен пойти на b* pattern, где он успешно находит соответствие регулярному выражению.

    Pattern: /((?>.*)|b*)[ac]/
    bbabbbabbbbc
    ^             -- Start matching. Look at first item in alternation: (?>.*)
    bbabbbabbbbc
                ^ -- First match of .*, due to greedy quantifier
                  -- The atomic grouping will disallow .* to be backtracked and rematched
    bbabbbabbbbc
                X -- [ac] cannot match
                  -- Backtrack to ()
                  -- (?>.*) is atomic, check the next possibility by alternation: b*
    bbabbbabbbbc
    ^             -- Starting to rematch with b*
    bbabbbabbbbc
      ^           -- First match with b*, due to greedy quantifier
    bbabbbabbbbc
       ^          -- [ac] matches, end of regex, a match is found
    

    последующие матчи будут продолжаться дальше.


"атомная группа" - это та, где регулярное выражение никогда не будет возвращаться назад. Итак, в вашем первом примере /a(?>bc|b)c/ Если bc чередование в групповых матчах, тогда он никогда не откажется от этого и попробует b чередование. Если вы немного измените свой первый пример, чтобы соответствовать "abcdabcc" тогда вы увидите, что он по-прежнему соответствует "abcc" в конце строки вместо "abc" в начале. Если вы не используете атомарную группу, то она может вернуться мимо bc и попробовать b чередование и в конечном итоге, соответствующие "abc" в начале.

Что касается второго вопроса, как это отличается, это просто перефразирование вашего первого вопроса.

и, наконец, атомарные группы не "называются" группами без захвата. Это не альтернативное название для них. Группы без захвата-это группы, которые не захватывают свое содержимое. Обычно при сопоставлении регулярного выражения со строкой можно получить все сопоставленные группы и если вы используете подстановку, вы можете использовать backreferences в подстановке как чтобы вставить захваченные группы там. Но группа без захвата не обеспечивает этого. Классическая группа без захвата -(?:pattern). Атомная группа также имеет свойство не захватывать, поэтому она называется группой без захвата.


недавно мне пришлось объяснить атомные группы кому-то еще, и я подумал, что я бы настроить и поделиться примером здесь.

считают the (big|small|biggest) (cat|dog|bird).

матчи жирным

  • большой пес
  • маленькая птица
  • самая большая собака
  • маленький кот

для первой строки движок regex найдет the. Это затем переходим к нашим прилагательным (big, small, biggest), он находит big. Подобрав "большой", он продолжает и находит свое пространство. Затем он смотрит на наших питомцев (cat, dog, bird) и находит cat, пропускает его, а находит dog.

для второй строки наше регулярное выражение найдет the. Он будет продолжать и смотреть на big, пропустите его, посмотрите и найдите small. Затем он находит " ". Он смотрит на "кошку", пропускает ее, смотрит на "собаку", пропускает ее и находит "птица."

для третьей строки наше регулярное выражение найдет the, Он продолжается и найти big что соответствует немедленное требование, и доходов. Он не может найти пространство, поэтому он откатывается (перематывает позицию к последнему сделанному выбору). Он пропускает big, смотрит на small и пропускает его. Он находит самый большой, который также соответствует немедленное требование. Затем он находит " ". Он смотрит на cat и пропускает его, и спички dog.

для четвертой строки наше регулярное выражение найдет the. Он будет продолжать смотреть на big, пропустите его, посмотрите и найдите small. Затем он находит " ". Он смотрит и соответствует cat.

Теперь рассмотрим the (?>big|small|biggest) (cat|dog|bird) Примечание ?> атомная группа по прилагательным.

матчи жирным

  • большой пес
  • маленькая птица
  • самая большая собака!--46-->
  • маленький кот

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

для третьей строки наше регулярное выражение найдет the, Он продолжается и находит "большой", который соответствует немедленное требование, и доходов. Он не может найти пространство, но атомная группа, будучи последним выбором, который сделал двигатель, не позволит это выбор для повторного рассмотрения (запрещает возврат.) Поскольку он не может сделать новый выбор, матч должен потерпеть неудачу, поскольку наше простое выражение не имеет другого выбора.

это только основные моменты. Двигателю не нужно было бы смотреть на всюcat знать, что это не соответствует dog, достаточно просто посмотреть на c. При попытке соответствовать птице,c на cat и d в собаке достаточно сказать двигателю, чтобы изучить другие варианты.

если так и было ...((cat|snake)|dog|bird), двигатель также, конечно, необходимо изучить змея, прежде чем он упал в предыдущей группе и исследовал собаку и птицу.

есть также много вариантов, которые двигатель не может решить, не пройдя мимо того, что не может показаться совпадением. Если у вас есть ((red)?cat|dog|bird), двигатель будет смотреть на "r", назад, обратите внимание на ? Квантор, игнорировать подгруппе (red), и ищите совпадение.