Ruby и красивый код #4
декабря 7, 2010 | Published in Ruby, Основы | 11 Comments
Это относительно вольный перевод с английского статьи
Предыдущие статьи из рубрики:
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]"
Наибольшая благодарность автору блога — ваши коментарии!
декабря 7, 2010 at 21:02 (#)
Ёлки! Это даже красиво (-:
Только вот я не понял 3ю идиому… вернее смысл понял, но вот описания, как с первой, нет… и в чем тогда различие? Ведь «|» — это тоже «ИЛИ»…
Спасибо всё равно (-:
Такие статьи гораздо лучше объясняют язык, чем все книжки вместе взятые
декабря 7, 2010 at 21:12 (#)
none, спасибо за спасибо ;-)
По поводу вопроса:
|| — оператор «ИЛИ» синоним or
|= — метод объекта Array, который делает вставку оригинальных элементов.
декабря 7, 2010 at 22:34 (#)
Ничего себе! Ну раз это аж цельный метод… Так понятнее, спасибо
декабря 7, 2010 at 23:30 (#)
none, я немного ошибся, метод на самом деле выглядит так:
декабря 7, 2010 at 23:48 (#)
Не вкурил #4, без идиом понятнее :)
декабря 8, 2010 at 00:52 (#)
Оператор | (или) в контексте множеств означает объединение. Так легче запомнить.
декабря 8, 2010 at 01:09 (#)
Scrill, на самом деле это дело предпочтений каждого человека. Мне например удобно пользоваться идиомами (т.н. синтаксическим сахаром) т.к. код становится короче, а когда я нахочу что-то непонятное, Гугль мне всегда помогает разобраться. Все они достаточно быстро запоминаются.
c0va23, маленькая неточность: не просто объединяет, а к значениям из первого массива добавляет те значения из второго, которых нет в первом. Причем добавленные значения добавляются в конец массива c в том порядке, в котором они следовали в массиве b.
декабря 10, 2010 at 13:40 (#)
Если кто не знал на сайте более красивая и удобная api документация по ruby и rails. Также на документация для ruby версии 1.9.1, а на есть 1.9.2.
декабря 23, 2010 at 00:12 (#)
Спасибо, таким вещам только из кода чужого можно научиться, а у вас еще с объяснениями, пишите еще.
января 2, 2011 at 17:17 (#)
идиому 4 еще можно так написать
user_id = (comments[:first][:user][:id] rescue nil) || «WTF»
января 3, 2011 at 11:10 (#)
ruby.freeman, отличный код!