ActiveSupport::Inflector::camelize — помощь в понимании регулярного выражения

Укороченная версия:

Мне довольно трудно понять два довольно сложных регулярных выражения в методе ActiveSupport::Inflector::camelize.

Это определение метода camelize:

def camelize(term, uppercase_first_letter = true)
  string = term.to_s
  if uppercase_first_letter
    string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
  else
    string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
  end
  string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
end

Мне трудно понять:

string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }

и:

string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')

Пожалуйста, объясните мне, что они означают. Спасибо.

Длинная версия

Это показывает, как я пытаюсь понять регулярное выражение и то, как я его интерпретирую. Было бы очень полезно, если бы вы могли пройти через это и исправить мои ошибки.

Для первого регулярного выражения

string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }

Судя по тому, что я вижу, inflections.acronym_regex относится к классу Inflections в модуле ActiveSupport::Inflector, а в методе initialize класса Inflections,

def initialize
  @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
end

acronym_regex назначается /(?=a)b/. Насколько я понимаю из http://www.ruby-doc.org/core-2.0.0/Regexp.html#class-Regexp-label-Anchors ,

(?=pat) - Positive lookahead assertion: ensures that the following characters match pat, but doesn't include those characters in the matched text

Таким образом, /(?=a)b/ гарантирует, что символ a находится внутри текста, но мы не включаем символ a в совпадающий текст, и то, что сразу следует за символом a, должно быть символом b. Другими словами, "abc" будет соответствовать этому регулярному выражению, но "bbc" не будет соответствовать этому регулярному выражению, а совпадающий текст для "abc" будет "b" (вместо "ab").

Таким образом, объединив значение inflections.acronym_regex в это регулярное выражение /^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/, я не знаю, какой из следующих двух результатов регулярного выражения:

A. /^(?:/(?=a)b/(?=\b|[A-Z_])|\w)/

B. /^(?:(?=a)b(?=\b|[A-Z_])|\w)/

хотя я думаю, что это B. Насколько я понимаю, (?: обеспечивает группировку без захвата, (?= означает положительное опережающее утверждение, \b соответствует границам слов, когда они находятся вне скобок, и соответствует возврату, когда внутри скобок. Таким образом, в английских терминах регулярное выражение B при сопоставлении с текстом найдет строку, начинающуюся с символа a, за которым следует символ b, и один из (1. backspace [что бы это ни значило] 2. любой символ верхнего регистра или подчеркивание 3. любой английский алфавитный символ, цифра или подчеркивание).

Однако мне кажется странным, что передача upper_case_first_letter = false функции camelize должна привести к тому, что она будет соответствовать строке, начинающейся с символов ab, учитывая, что это не похоже на то, как ведет себя функция camelize.

Для второго регулярного выражения

string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')

Регулярное выражение:

/(?:_|(\/))([a-z\d]*)/i

Я предполагаю, что это регулярное выражение будет соответствовать подстроке, начинающейся с _ или /, за которой следует 0 или более (верхние или строчные буквы английского алфавита или цифры). Более того, для первой группы (?:_|(\/)) независимо от того, соответствует ли она _ или /, группа захвата ([a-z\d]*) всегда будет считаться второй группой. Я понимаю ту часть, где блок пытается найти inflections.acronyms[$2] и в случае неудачи делает $2.captitalize.

Поскольку (?: означает группировку без захвата, каково значение $1, когда мы сопоставляем _? Это все еще _ ? А что касается части .gsub('/', '::'), я предполагаю, что она применяется для каждого совпадения в начальном gsub, а не применяется ко всей строке после выполнения внешнего вызова gsub?

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

Спасибо.


person yanhan    schedule 17.12.2013    source источник
comment
Пожалуйста, сформулируйте свой вопрос кратко.   -  person sawa    schedule 17.12.2013
comment
@sawa, я мог бы отредактировать его так, чтобы он стал очень кратким, но тогда поможет ли это людям, которые просматривают вопрос, увидеть, в чем заключаются мои трудности с пониманием регулярного выражения 2? Думаю, нет.   -  person yanhan    schedule 17.12.2013
comment
В любом случае я думаю, что добавлю раздел tldr.   -  person yanhan    schedule 17.12.2013


Ответы (1)


Однако мне кажется странным, что передача upper_case_first_letter = false функции camelize должна привести к тому, что она будет соответствовать строке, начинающейся с символов ab, учитывая, что это не похоже на то, как ведет себя функция camelize.

?: здесь действует как . и соответствует строке (т.е. одному символу), но группировки нет, поэтому совпадение находится в $&.

Поскольку (?: означает группировку без захвата, каково значение $1, когда мы сопоставляем _ ? Это все еще _ ?

Это nil, так как захвата нет. Значение находится в $2

И для части .gsub('/', '::') я предполагаю, что она применяется для каждого совпадения в исходном gsub, вместо того, чтобы применяться ко всей строке после выполнения внешнего вызова gsub?

Он применяется к общему результату, поскольку gsub с блоком возвращает строку, а gsub('/', '::') находится вне блока.

person Bart    schedule 02.01.2014