13. Регулярни изрази, част 2. Преглед на решения. Други

13. Регулярни изрази, част 2. Преглед на решения. Други

13. Регулярни изрази, част 2. Преглед на решения. Други

2 декември 2013

Днес

Първи тест

Ruby Monk

Ruby 2.1-preview2

Замразени низове

String#freeze

Преди:

"larodi"f.object_id       # => 70328402714000
"larodi"f.object_id       # => 70328402714000

"larodi".freeze.object_id # => 70366570584360
"larodi".freeze.object_id # => 70366570566320

Сега:

"larodi"f.object_id       # SyntaxError

"larodi".freeze.object_id # => 70321298740820
"larodi".freeze.object_id # => 70321298740820

Exception#cause

Ruby 2.1-preview2

Upgrade NAO!

Кратък преговор от миналия път

%(), %q(), %r(), %w(), %i()...

stack.push "вметка"

Бърз пример за създаване на низове:

%{foobar}                               # "foobar"
%(larodi, something "quoted")           # "larodi, something \"quoted\""
%(Interpolation? #{'Why, yes!'.upcase}) # "Interpolation? WHY, YES!"

Създаване на низове с %()

особености

%(Parens: ( or ) - works.) # "Parens: ( or ) - works."
%<Parens: < or > - works.> # "Parens: < or > - works."
%[Parens: [ or ] - works.] # "Parens: [ or ] - works."
%{Parens: { or } - works.} # "Parens: { or } - works."

Heredocs

stack.push "вметка"

Още един начин за дефиниране на низове, удобен за по-дълги и многоредови текстове:

template = <<HTML
<html>
<head>
  <title>K-pop</title>
</head>
<body>
  Sparkly
</body>
</html>
HTML

Идентация на heredocs

query = <<-SQL
          DROP VIEW ...;
          CREATE VIEW ...
        SQL

Забележка: Идентацията на текста си остава.

Heredocs вмъкнати в друг код

Могат да се вмъкват в нормален код:

scream = <<TEXT.strip.upcase
  This is a garrage, don't park! I'll slash your tires and scratch your paint.
TEXT

# => "THIS IS A GARRAGE, DON'T PARK! I'LL SLASH YOUR TIRES AND SCRATCH YOUR PAINT."

Или така:

strings = [<<END, 'second', 'third']
First string
END

# => ["First string\n", "second", "third"]

Наслагване на heredocs

Дори това работи:

string = [<<ONE, <<TWO, <<THREE]
  the first thing
ONE
  the second thing
TWO
  and the third thing
THREE

# => ["  the first thing\n", "  the second thing\n", "  and the third thing\n"]

Варианти на %-синтаксиса

МодификаторЗначение
%q( )Низ с единични кавички (без интерполация)
%Q( )Низ с двойни кавички (с интерполация)
%r( )Регулярен израз с интерполация; флаговете се слагат след затварящия ограничител
%i( )Списък от символи без интерполация (Ruby 2.0+)
%I( )Списък от символи с интерполация (Ruby 2.0+)
%w( )Списък от думи без интерполация
%W( )Списък от думи с интерполация
%x( )Shell-команда с интерполация

Регулярни изрази с %r()

Сега разбирате защо работи това:

if %r(^(/\w+)*/(?<basename>\w+)) =~ '/some/path/here'
  basename # "here"
end

Интерполация в регулярни изрази

Между другото, регулярните изрази поддържат интерполация по подразбиране:

name      = /[^@]+/
host      = /\w+\.(com|net|org)/

email     = /#{name}@#{host}/ # /(?-mix:[^@]+)@(?-mix:\w+\.(com|net|org))/

Понятия и терминология

преговор

Референции към групи в рамките на шаблона

Рекурсивни групи

/(\w+), \1/.match    'testing, twice'   # nil
/(\w+), \g<1>/.match 'testing, twice'   # #<MatchData "testing, twice" 1:"twice">

Валидация на вложени скоби с регулярен израз

едната задача от миналата лекция

Валидирайте изрази от следния тип за правилно отворени/затворени скоби:
  • (car (car (car ...)))
  • Например: (car (car (car (car list))))
  • Целта е израз, чийто резултат да може да се ползва в условен оператор (true/false-еквивалент)
  • Можете да ползвате произволни методи от класа Regexp
  • И регулярен израз, разбира се

Примерно решение

с рекурсивни групи

validator = /^(\(car (\g<1>*|\w*)\))$/

valid   = '(car (car (car (car list))))'
invalid = '(car (car (car list))'

validator.match(valid)   # #<MatchData "(car (car (car (car list))))" 1:"(car (car (car (car list))))" 2:"list)))">
validator.match(invalid) # nil

Валидация на просто число с регулярен израз

другата задача от миналия път

Да се напише кратък Ruby expression, който проверява дали дадено число е просто или не, посредством употреба на регулярен израз. Резултатът от изпълнението му трябва да е true за прости числа и false за всички останали. Неща, които можете да ползвате:
  • Самото число, разбира се.
  • Произволни методи от класа Regexp
  • Подходящ регулярен израз (шаблон)
  • Текстовия низ '1'.
  • String#*.
  • Някакъв условен оператор (например if-else или ? … : …)
  • true, false, ...

Решение на проверката за просто число с РИ

Look-ahead и look-behind

/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favours the <b>bold</b>") # #<MatchData "bold">

Работа с MatchData-обекти

Най-полезни методи на MatchData-обектите

/(\w+)/.match('Some words')[1]              # "Some"
/(\w+)/.match('Some words').begin(1)        # 0
/(?<id>\d+)/.match('ID: 12345')[:id]        # "12345"
/(?<id>\d+)/.match('ID: 12345').begin(:id)  # 4

#pre_match и #post_match методи

на MatchData-обектите

match = /(?<number>\d+)/.match 'ID: 12345 (new)'

match[:number]    # "12345"
match.pre_match   # "ID: "
match.post_match  # " (new)"

Специалните променливи

case с регулярни изрази

работи благодарение на Regexp#===

html = '<h1>Header</h1>' # или:
html = '<img src="http://my/image.src" alt="Kartman Makes Burgers" />'

case html
  when /(<h(\d)>)(.+)<\/h\2>/
    {header: $3, size: $2}
  when /<a\s+href="([^"]+)">([^<]+)<\/a>/
    {url: $1, text: $2}
  when /<img\s+src="([^"]+)"\s+alt="([^"]+)"\s*\/>/
    {image: $1, alt: $2}
  else
    'unrecognized tag'
end

# {:image=>"http://my/image.src", :alt=>"Kartman Makes Burgers"}

Методи в String

свързани с регулярни изрази

Пример със String#gsub

плюс групи и блок

'SomeTitleCase'.gsub /(^|[[:lower:]])([[:upper:]])/ do
  [$1, $2.downcase].reject(&:empty?).join('_')
end

# "some_title_case"

Encoding

Цитат от документацията:

A regexp can be matched against a string when they either share an encoding, or the regexp’s encoding is US-ASCII and the string’s encoding is ASCII-compatible.

Encoding & Unicode

Rubyのお父さんはまつもとゆきひろさんです。
unicode_test = 'Rubyのお父さんはまつもとゆきひろさんです。'

/は[[:alpha:]]+さん/.match unicode_test # #<MatchData "はまつもとゆきひろさん">

Граници на думи в Unicode-текст

Граници на думи в Unicode-текст

пример

Например:

'Ruby no otousan ha Matsumoto Yukihiro san desu.'.gsub(/(\b[[:alpha:]]+\b)/) { "[#{$1}]" }
# "[Ruby] [no] [otousan] [ha] [Matsumoto] [Yukihiro] [san] [desu]."

Но:

'Rubyのお父さんはまつもとゆきひろさんです。'.gsub(/(\b[[:alpha:]]+\b)/) { "[#{$1}]" }
# "[Rubyのお父さんはまつもとゆきひろさんです]。"

Флагове на шаблоните

Условия в шаблоните

Ново в Ruby 2.0

regexp = /^([A-Z])?[a-z]+(?(1)[A-Z]|[a-z])$/

regexp =~ "foo"   # 0
regexp =~ "foO"   # nil
regexp =~ "FoO"   # 0

Документация

Въпроси?

За предпочитане по темата за регулярните изрази.

Десетото предизвикателство

Нашето решение

Примерното решение, което даваме, е на Петко:

class String
  def longest_sequence
    return [] if empty?
    scan(/(.)(\1*)/).map(&:join).group_by(&:length).max.last.map(&:chr).uniq
  end
end

Вашите решения

Да ги разгледаме.

Въпроси