Regexp.last_match - зачем это полезно?

В книге " Всеобъемлющее рубиновое программирование курса" я столкнулся с главой, где автор (Джордан Хадженс) описывает это как:

"Последнее, что мы собираемся попробовать, - вернуть все целые значения из предложения".

И он делает это так:

string = "The quick 12 brown foxes jumped over 10 lazy dogs"
p string.to_enum(:scan, /\d+/).map { Regexp.last_match }

И он возвращает:

=> [#<MatchData "3">, #<MatchData "34">, #<MatchData "23">]

Интересно, почему/когда этот Regexp.last_match может быть использован или лучше сказать - почему этот способ не эффективнее, чем:

p string.to_enum(:scan, /\d+/).map { |i| p i } 

Это выводит только массив целых чисел и кажется для меня более эффективным способом получения этих чисел.

Возможно, кто-нибудь может объяснить, каковы были причины, по которым автор мог выбрать Regesp.last_match?

Ответ 1

Это отличный трюк (читай: взломать).

string = "The quick 12 brown foxes jumped over 10 lazy dogs"
p string.to_enum(:scan, /\d+/).map { Regexp.last_match }

Дело в том, что нет yield экземпляров MatchData из String#scan внутри String#scan.

p string.to_enum(:scan, /\d+/).map { |i| p i } 

не имеет большого смысла, вы, вероятно, имели в виду:

p string.to_enum(:scan, /\d+/).map(&:itself) # or { |i| i } # or .to_a

или даже

p string.scan(/\d+/) 

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

Ответ 2

Здесь более подробное, но, возможно, более чистое решение, если вы хотите, чтобы перечисление MatchData экземпляров:

class String
  def matches(regex)
    position = 0
    Enumerator.new do |yielder|
      while match = regex.match(self, position)
        yielder << match
        position = match.end(0)
      end
    end
  end
end

string = 'The quick 12 brown foxes jumped over 10 lazy dogs'
p string.matches(/\d+/).to_a
# [#<MatchData "12">, #<MatchData "10">]
p (2**1000000).to_s.matches(/(\d)\1{5}/).first(2)
# [#<MatchData "444444" 1:"4">, #<MatchData "888888" 1:"8">]

Если вы не хотите патч обезьяны String, вы можете определить этот метод в Regex или как автономный метод с параметрами String и Regex.