Ruby и красивый код #4

декабря 7, 2010  |  Published in Ruby, Основы  |  11 Comments

Это относительно вольный перевод с английского статьи

ruby

Предыдущие статьи из рубрики:
Ruby и красивый код #1
Ruby и красивый код #2
Ruby и красивый код #3

Что же такое идиома?
Идиома — это что-то специфическое в языке. Идиомы Ruby — это программистские техники которые специфичны для Ruby и позволяют писать на Ruby небольшие фрагменты кода, которые, если попытаться переписать на менее лаконичном языке, потеряют свою красоту и могут превратиться во что-то монструозное.

Зачем изучать идиомы в Ruby, если можно писать код и без них?

Во-первых идиомы — это лаконичный код, а во-вторых вы ведь не сплошь и рядом пользуетесь своими велосипедами? Вы наверняка используете чужие библиотеки и rubygem’ы и плагины и читая чужой код вы наверняка наткнетесь на идиомы и техники метапрограммирования, таким образом без знания идиом языка Ruby и техник метапрограммирования вы не сможете понять чужого кода.

Идиома 1

Используем ее когда нам необходимо назначить переменной какое-либо значение, при условии, что переменная равна nil.

Решаем задачу без идиом:

# Если переменная Car_model пуста, то назначаем переменной значение benz.
If not car_model
  сar_model = :benz
end 

Идиоматический способ:

 
# Используем оператор || - "или". Можно прочитать как: car_model или-равно :bacon, условие или включается, если car_model будет nil.
сar_model  || = :bacon

Идиома 2

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

Решаем задачу без идиом:

# Здесь мы проверяем равна ли переменная val какому либо числу.
if temperature = = 36.4 || val = = 36.6 || val = = 36.9
  puts “I have good health today!”
end

Идиоматический способ:

# Метод include? возвращает true если массив содержит значение temperature:
if  [36.4, 36.6, 36.9].include?(temperature)
  puts “I have good health today!”
end

Идиома 3

Допустим у нас есть массив nationality содержащий национальности и нам необходимо добавить в него новую национальность, но только при условии, что ее еще нет в массиве дабы избежать повторений.

Решаем задачу без идиом:

nationality = ['american', 'british']
nationality += ['italian'] unless nationality.include?('italian')

Идиоматический способ:

nationality = ['american', 'british']
nationality | = ['italian'] #=> ['american', 'british', 'italian']

Идиома 4

В четвертом примере мы рассмотрим случаи где у нас есть многоуровневая структура и мы хотим получить доступ к какому-либо элементу этой структуры (хэша), но только после ряда проверок.

Решаем задачу без идиом:

# здесь мы используем вложенные условия If для проверки данных перед их использованием
User_id = nil
if comments
  if comments[:first]
    if comments[:first][:user]
      user_id = comments[:first][:user][:id]
    end
  end
end

Идиоматический способ:

Здесь используются оператор && — «И». Ruby понимает это так: он пробегает по всей ветви comments и проверяет существует ли указанный в условии элемент, если нет, то возвратит нам nil. В примере неидиоматического решения задачи мы использовали user_id = nil потому, что это то, что нам нужно вернуть если на одной из ступеней вложенных if мы получим false, т.е. элемент будет не найден. Если вы хотите передать какое-либо другое значение, кроме nil в случае не прохождения проверки, то вы можете просто добавить это значение в конец нижеприведенной идиомы как расширение.

User_id = comments && coments.first && comments.first.user && comments.first.user.id
comments = {first: {user: {name: "Vasya", id: 1}}, second: {} }
user_id = comments && comments[:first] && comments[:first][:user] && comments[:first][:user][:id]

p user_id #=> 1

#Присваиваем любое другое значение кроме nil:

user_id = "WTF!" unless comments && comments[:second] && comments[:second][:user] && comments[:second][:user][:id]

p user_id #=> "WTF!"

# Автор оригинала намекнул на более короткий вариант:

user_id = comments && comments[:second] && comments[:second][:user] && comments[:second][:user][:id] || "WTF!"

p user_id #=> "WTF!"

Идиома 5

В пятом примере предполагается что у нас есть массив и мы хотим присвоить некоторым переменным определенные элементы массива.

Неидиоматический способ:

a = cars[0]
b = cars[1]

Идиоматический способ:

a, b = *cars

«Звездочка» * — это унарный оперетор, который как бы преобразует массив в набор аргументов. Этот оперетор так же может использоваться для создания массивов: в примере ниже показано, как переменной a присваивается значение 0 элемента, а переменной b — массив со всеми остальными значениями из массива car:

cars =[:bmw, :ferrari, :mercedes]
a, *b = cars

p a.inspect #=> ":bmw"
p b.inspect#=> "[:ferrari, :mercedes]"

Вы наверняка знакомы с тем, как создаются методы с произвольным числом аргументов, так вот там *arg получает аргументы в самую последнуюю очередь (загребает все оставшиеся аргументы в массив), здесь же порядок расположения имеет значение, поменяв местами a и *b мы получим следующую картину:

*b, a = cars

p a.inspect #=> ":mercedes"
p b.inspect#=> "[:bmw, :ferrari]"

Наибольшая благодарность автору блога — ваши коментарии!

Tags: , ,

Responses

  1. says:

    декабря 7, 2010 at 21:02 (#)

    Ёлки! Это даже красиво (-:
    Только вот я не понял 3ю идиому… вернее смысл понял, но вот описания, как с первой, нет… и в чем тогда различие? Ведь «|» — это тоже «ИЛИ»…
    Спасибо всё равно (-:
    Такие статьи гораздо лучше объясняют язык, чем все книжки вместе взятые

  2. admin says:

    декабря 7, 2010 at 21:12 (#)

    none, спасибо за спасибо ;-)

    По поводу вопроса:

    || — оператор «ИЛИ» синоним or

    |= — метод объекта Array, который делает вставку оригинальных элементов.

  3. says:

    декабря 7, 2010 at 22:34 (#)

    Ничего себе! Ну раз это аж цельный метод… Так понятнее, спасибо

  4. admin says:

    декабря 7, 2010 at 23:30 (#)

    none, я немного ошибся, метод на самом деле выглядит так:

    a, b = [1,2,3], [1,2,4,5,6] 
    c = a | b #получаем новый массив: c => [1,2,3,4,5,6]
    
    # |= - присваивает результат метода | переменной слева от метода, т.е. a, т.е. это эквивалентно:
    
    a = a | b
    
    

  5. Scrill says:

    декабря 7, 2010 at 23:48 (#)

    Не вкурил #4, без идиом понятнее :)

  6. c0va23 says:

    декабря 8, 2010 at 00:52 (#)

    Оператор | (или) в контексте множеств означает объединение. Так легче запомнить.

  7. admin says:

    декабря 8, 2010 at 01:09 (#)

    Scrill, на самом деле это дело предпочтений каждого человека. Мне например удобно пользоваться идиомами (т.н. синтаксическим сахаром) т.к. код становится короче, а когда я нахочу что-то непонятное, Гугль мне всегда помогает разобраться. Все они достаточно быстро запоминаются.

    c0va23, маленькая неточность: не просто объединяет, а к значениям из первого массива добавляет те значения из второго, которых нет в первом. Причем добавленные значения добавляются в конец массива c в том порядке, в котором они следовали в массиве b.

  8. dre3k says:

    декабря 10, 2010 at 13:40 (#)

    Если кто не знал на сайте более красивая и удобная api документация по ruby и rails. Также на документация для ruby версии 1.9.1, а на есть 1.9.2.

  9. Shtirlic says:

    декабря 23, 2010 at 00:12 (#)

    Спасибо, таким вещам только из кода чужого можно научиться, а у вас еще с объяснениями, пишите еще.

  10. ruby.freeman says:

    января 2, 2011 at 17:17 (#)

    идиому 4 еще можно так написать
    user_id = (comments[:first][:user][:id] rescue nil) || «WTF»

  11. admin says:

    января 3, 2011 at 11:10 (#)

    ruby.freeman, отличный код!

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.