11. Регулярни изрази, част 2. Ruby-стил в и около трета задача

11. Регулярни изрази, част 2. Ruby-стил в и около трета задача

11. Регулярни изрази, част 2. Ruby-стил в и около трета задача

21 ноември 2011

Днес

Терминология

припомняне от предишната лекция по темата

Групи

и прихващане

Символите ( и ) се използват за логическо групиране на части от шаблона с цел:

Референции към групи

Текстът, който match-ва частта на шаблона, оградена в скоби, може да се достъпва:

Именовани групи

/(?<date>\d{4}-\d{2}-\d{2})/.match 'Today is 2011-11-08, Tuesday.' # #<MatchData "2011-11-08" date:"2011-11-08">

Референции към групи

в рамките на шаблона

Примери за референции към групи

/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/.match 'Today is 2011-11-08, Tuesday.'
# #<MatchData "2011-11-08" year:"2011" month:"11" day:"08">

/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\11/.match 'Regular expressions'
# #<MatchData "ular express" 1:"u" 2:"l" 3:"a" 4:"r" 5:" " 6:"e" 7:"x" 8:"p" 9:"r" 10:"e" 11:"s">
/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\k<11>1/.match 'Regular express1ions'
# #<MatchData "ular express1" 1:"u" 2:"l" 3:"a" 4:"r" 5:" " 6:"e" 7:"x" 8:"p" 9:"r" 10:"e" 11:"s">

Уточнение относно референциите

в рамките на шаблона

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

/(?<word>\w+), \k<word>/.match 'testing, testing' # #<MatchData "testing, testing" word:"testing">

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

/(\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)   ? true : false # true
validator.match(invalid) ? true : false # 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)"

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

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

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

пример

log_entry = "[2011-07-22 15:42:12] - GET / HTTP/1.1 200 OK"

if log_entry =~ /\bHTTP\/1.1 (\d+)/
  request_status = $1.to_i # 200
else
  raise "Malformed log entry!"
end

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

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

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

case html
when /(<h(\d)>)(.+)<\/h\2>/                       then {header: $3, size: $2}
when /<a\s+href="([^"]+)">([^<]+)<\/a>/           then {url: $1, text: $2}
when /<img\s+src="([^"]+)"\s+alt="([^"]+)"\s*\/>/ then {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"

Unicode

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

/は[[:alpha:]]+さん/.match unicode_test # #<MatchData "はまつもとゆきひろさん"># ~> -:1: invalid multibyte char (US-ASCII)
# ~> -:1: invalid multibyte char (US-ASCII)

Граници на думи в 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-стил

Следващите слайдове са базирани основно на код, видян в първия ви опит да решите трета задача

Skeptic

Константи и имена на класове

Класови методи

Kernel#sprintf и String#%

Без излишни скоби

Whitespace

ich bin eine whitespace nazi

Whitespace

OMG идентация...

OMG Indentation

Излишни неща

if (10...20).include?(cardinal) then
  something...
end

Странен код

серия първа

Не:

if not promotion.empty?
  new_promo.promo[new_product] = promotion.to_a
  make_promoted(new_product)
  else has_promotion[new_product] = false
end

Поне да е нещо такова:

if promotion.empty?
  has_promotion[new_product] = false
else
  new_promo.promo[new_product] = promotion.to_a
  make_promoted(new_product)
end

Странен код

Не ползвайте unless с else! Ако ви се налага, ползвайте if-else

Плюс това, идентация...

unless inventory.new_promo.promo[product].kind_of? Hash
  inventory.new_promo.get_one_free(product, one_s_price, quantity, value)
else inventory.new_promo.promo_type(product, one_s_price, quantity, pack, value)
end

Странен код

серия втора, част първа

Ох...

def execute(amount,price)
  return calculate_get_one_free(amount,price) if @name == :get_one_free

  return calculate_package(amount,price) if @name == :package

  return calculate_threshold(amount,price) if @name == :threshold

end

Ма-а-алко по-добре:

def execute(amount, price)
  case @name
  when :get_one_free then calculate_get_one_free(amount, price)
  when :package      then calculate_package(amount, price)
  when :threshold    then calculate_threshold(amount, price)
  end
end

Странен код

серия втора, част втора (става касова продукция...)

Или пък:

def execute(amount, price)
  send "calculate_#{@name}", amount, price
end

За това ще си говорим по-нататък; не е непременно добра практика :)

Странен код

вече е трилогия

class ShopCar

  def shop_car()
    @shop_car = {}
  end

Странен код

Доктор "Ох, боли ме сърцето..."

t = '0.00'.to_d
helper = inventory.all_coupons[name_of_coupon]
t = helper[0] == :percent ? cpn.percent(cena, helper[1]) : cpn.amount(cena, helper[1])
# съжалявам за съкращенията, но трябваше да се сместя в sceptic

Можете повече, вярваме във вас :)

Странен код

def function(w, x, y, z)
  - ( ( ( w - x ) * y.to_d ) * ( z / 100 ))
end

Keeewl... А какво точно правеше този метод? Хм...

Странен код

class Numeric
  def ordinal
    cardinal = self.to_i.abs
    if (10...20).include?(cardinal) then
      cardinal.to_s << 'th'
    else
      cardinal.to_s << %w{th st nd rd th th th th th th}[cardinal % 10]
    end
  end
end

Странен код

не така

def package(product, one_s_price, quantity, pack, value)
  out_of_pack = quantity % pack
  if  promo[product].has_key?(:package) and quantity >= pack
    return function(quantity, out_of_pack, one_s_price, value)
    else return '0.00'.to_d
  end
end

Странен код

малко по-добре е така

def package(product, one_s_price, quantity, pack, value)
  out_of_pack = quantity % pack

  if promo[product].has_key?(:package) and quantity >= pack
    a_better_named_function(quantity, out_of_pack, one_s_price, value)
  else
    '0.00'.to_d
  end
end

Владеене на езика

Coding-style Guide в Ruby

произведение на г-н Бацов и с-ие

Въпроси