четверг, 25 февраля 2010 г.

Интересные приёмы в Ruby, которые вы можете использовать в своём коде

http://habrahabr.ru/blogs/ruby/48740/
В этом посте я хочу представить вам 21 интересную особенность Ruby. Многие из этих особенностей могут быть не очевидны новичку, в то время как более опытный разработчик использует их каждый день. Для выполнения примеров кода удобнее всего использовать «интерактивный» Ruby — irb. Если же у вас ещё нет своего Ruby, то воспользуйтесь онлайн интерактивным интерпретатором. Итак, приступим.


1. Быстрое получение совпадений из регулярного выражения


Чтобы получить данные из текста используя регулярные выражения, как правило применяют метод match. Сейчас я покажу как использовать его наиболее эффективно:

email = "Fred Bloggs "
email.match(/<(.*?)>/)[1]            # => "fred@bloggs.com"
email[/<(.*?)>/, 1]                  # => "fred@bloggs.com"
email.match(/(x)/)[1]                # => NoMethodError [:(]
email[/(x)/, 1]                      # => nil
email[/([bcd]).*?([fgh])/, 2]        # => "g"

В комментариях мне делают замечание, что тема регулярных выражений не раскрыта. Раскрываю:
name, mail = *email.match(/(.+)\s<(.*)>/)[1..2]
puts name               # "Fred Bloggs"
puts mail               # "fred@bloggs.com"


2. Синоним для Array#join


Многие знают, что если использовать метод Array#* для «умножения» массива на число, то массив будет увеличен в заданное количество раз путём дублирования его элементов. Но но всем известно, что если вместо числа, «умножить» массив на строку, то это сработает как метод Array#join:
%w{this is a test} * ", "                 # => "this, is, a, test"
h = { :name => "Fred", :age => 77 }
h.map { |i| i * "=" } * "\n"              # => "age=77\nname=Fred"



3. Быстрое форматирование десятичных чисел.


Если вы хотите сделать форматированный вывод, например, для указания цен, скорее всего будете использовать метод sprintf, но не спешите, Ruby предлагает более интересную возможность:
money = 9.5
"%.2f" % money                       # => "9.50"


4. Окружаем текст тегами


Конечно предыдущий пример можно использовать не только для чисел, но и для строк:
"[%s]" % "same old drag"             # => "[same old drag]"

И даже для массивов:
x = %w{p hello p}
"<%s>%s" % x                    # => "hello
"


5. Удаляем директорию со всем содержимым


Нет-нет, не спешите писать код на bash, в Руби есть библиотека fileutils:
require 'fileutils'
FileUtils.rm_r 'somedir'


6. Разбиваем массивы и хэши


Можно использовать «*» чтобы «разбить» перечисляемые типы данных (массивы и хэши). Чтобы было понятно о чём идёт речь, вот несколько примеров:
a = %w{a b}
b = %w{c d}
[a + b]                              # => [["a", "b", "c", "d"]]
[*a + b]                             # => ["a", "b", "c", "d"]

a = { :name => "Fred", :age => 93 }
[a]                                  # => [{:name => "Fred", :age =>93}]
[*a]                                 # => [[:name, "Fred"], [:age, 93]]

a = %w{a b c d e f g h}
b = [0, 5, 6]
a.values_at(*b).inspect              # => ["a", "f", "g"]

Получаем из массива хэш:
fruit = ["apple","red","banana","yellow"] # ["apple", "red", "banana", "yellow"]
Hash[*fruit]                              # {"apple"=>"red", "banana"=>"yellow"}


7. Убираем объявление переменных.


Вместо объявления переменных с каким-то, зачастую пустым, начальным значением, можно это сделать «на лету». Таким образом можно совместить объявление переменной и какие-то манипуляции над ней. Приём особенно полезен, когда не известно, была ли объявлена эта переменная раньше:
(z ||= []) << 'test'


8. Использование не строковых ключей хэша


Такое применение можно встретить не часто, но иногда это может оказаться действительно полезно:
does = is = { true => 'Yes', false => 'No' }
does[10 == 50]                       # => "No"
is[10 > 5]                           # => "Yes"


9. Логические and и or могут объединить операторы в одну строку


Многие разработчики используют это приём чтобы сократить код, убрав ненужное разбиение на строки:
queue = []
%w{hello x world}.each do |word|
  queue << word and puts "Added to queue" unless word.length <  2
end
puts queue.inspect

# Output:
#   Added to queue
#   Added to queue
#   ["hello", "world"]


10. Выполнение кода, только если если файл запущен непосредственно


Вы можете использовать один и тот же код как в качестве самостоятельного скрипта, так и библиотеки. Тогда у вас может возникнуть необходимость выполнять части кода только если они использованы как самостоятельный скрипт или наоборот, подключаются с помощью require:
if __FILE__ == $0
  # Этот участок будет выполнен, только если файл выполнен как самостоятельная программа
end


11. Быстрое массовое присвоение


Что может быть проще:
a, b, c, d = 1, 2, 3, 4


12. Диапазоны вместо нескольких сравнений


Больше никаких if x > 1000 && x < 2000, теперь можно писать так:
year = 1972
puts  case year
        when 1970..1979: "Семидесятые"
        when 1980..1989: "Восьмидесятые"
        when 1990..1999: "Девяностые"
      end



13. Перечисляемые структуры вместо повторения кода


%w{rubygems daemons eventmachine}.each { |x| require x }


14. Тернарный оператор


О тернарном операторе обычно узнают на ранних этапах изучения Руби, но почему-то используют не часто. Тернарный оператор — не панацея, но иногда способен сделать многое проще:
puts x == 10 ? "x равно десяти" : "x не равно десяти"

# Конечно, можно сделать и присвоение:
LOG.sev_threshold = ENVIRONMENT == :development ? Logger::DEBUG : Logger::INFO


15. Несколько тернарных операторов


Безусловно, это может вызвать проблемы с читаемостью кода, но всё же:
qty = 1
qty == 0 ? 'none' : qty == 1 ? 'one' : 'many'

Да, немного запутанно… Так будет понятнее:
(qty == 0 ? 'none' : (qty == 1 ? 'one' : 'many'))


16. Избавляемся от избыточных логических выражений


Частенько можно увидеть что-нибудь вроде этого:
def odd?(x)
  if x % 2 == 0
    return false
  else
    return true
  end
end

Но это слишком длииино! Воспользуемся тернарным оператором:
def odd?(x)
  # Кстати, return можно опустить
  x % 2 == 0 ? false : true
end

Уже неплохо, но это тоже длинно, зачем нам использовать тернарный оператор, если «==» и так возвращает true или false:
def odd?(x)
  x % 2 != 0
end


17. Полный backtrace исключения


def do_division_by_zero; 5 / 0; end
begin
  do_division_by_zero
rescue => exception
  puts exception.backtrace
end


18. Перебираем в итераторе как одиночные объекты, так и перечисляемые


[*items].each do |item|
  # ...
end

В этом примере, если items — одиночный объект, то он будет преобразован в массив, благодаря заключению в «[]», если же это массив, то с ним ничего не произойдёт, потому что «[]» и «*» скомпенсируют друг-друга.

19. Перед блоком rescue не обязательно писать begin


def x
  begin
    # ...
  rescue
    # ...
  end
end

def x
  # ...
rescue
  # ...
end


20. Блоки комментариев


Для комментирования можно использовать не только «#», но и такую конструкцию:
puts "x"
=begin
  this is a block comment
  You can put anything you like here!

  puts "y"
=end
puts "z"


21. Rescue для спасения


Можно использовать однострочную форму rescue когда важно во что бы то ни стало вернуть значение:
h = { :age => 10 }
h[:name].downcase                         # ERROR
h[:name].downcase rescue "No name"        # => "No name"


Бонус 22. Параметр, заданный по-умолчанию


Как можно опознать, задан ли параметр метода вызывающей программой или было взято значение по-умолчанию.
Один из возможных вариантов:
def foo(a, b=(flag=true; 666))
  puts "b равно #{b}, flag равно #{flag.inspect}"
end

foo 100      # b равно 666, flag равно true
foo 100, 666 # b равно 666, flag равно nil
foo 100, 400 # b равно 400, flag равно nil


Бонус 23. Переводим из 10 системы счисление в любую другую с базисом от 2 до 36


1234567890.to_s(2)  # "1001001100101100000001011010010"
1234567890.to_s(8)  # "11145401322"
1234567890.to_s(16) # "499602d2"
1234567890.to_s(24) # "6b1230i"
1234567890.to_s(36) # "kf12oi"

Комментариев нет:

Отправить комментарий

Ярлыки

Архив блога