Общие рубиновые идиомы
Одна вещь, которую я люблю в рубине, состоит в том, что в основном это очень читаемый язык (что отлично подходит для самодокументирующего кода)
Однако, вдохновленный этим вопросом: объяснил Ruby Code
и описание того, как ||=
работает в рубине, я думал о рубиновых идиомах, которые я не использую, так как, честно говоря, я не полностью их понимаю.
Итак, мой вопрос, аналогичный примеру из упомянутого вопроса, какие общие, но не очевидные, рубиновые идиомы, мне нужно знать, чтобы быть действительно опытным рубиновым программистом?
Кстати, из упомянутого вопроса
a ||= b
эквивалентно
if a == nil || a == false
a = b
end
(Спасибо Ян Террелл за исправление)
Изменить: Оказывается, этот момент не является абсолютно бесспорным. На самом деле правильное расширение
(a || (a = (b)))
Посмотрите эти ссылки, почему:
Благодаря Jörg W Mittag для указания этого.
Ответы
Ответ 1
Предложение magic if, которое позволяет использовать тот же файл в качестве библиотеки или script:
if __FILE__ == $0
# this library may be run as a standalone script
end
Упаковка и распаковка массивов:
# put the first two words in a and b and the rest in arr
a,b,*arr = *%w{a dog was following me, but then he decided to chase bob}
# this holds for method definitions to
def catall(first, *rest)
rest.map { |word| first + word }
end
catall( 'franken', 'stein', 'berry', 'sense' ) #=> [ 'frankenstein', 'frankenberry', 'frankensense' ]
Синтаксический сахар для хэшей в качестве аргументов метода
this(:is => :the, :same => :as)
this({:is => :the, :same => :as})
Инициализаторы хеширования:
# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}
Синтаксис метакласса
x = Array.new
y = Array.new
class << x
# this acts like a class definition, but only applies to x
def custom_method
:pow
end
end
x.custom_method #=> :pow
y.custom_method # raises NoMethodError
переменные экземпляра класса
class Ticket
@remaining = 3
def self.new
if @remaining > 0
@remaining -= 1
super
else
"IOU"
end
end
end
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> "IOU"
Блоки, procs и лямбды. Живи и дыши их.
# know how to pack them into an object
block = lambda { |e| puts e }
# unpack them for a method
%w{ and then what? }.each(&block)
# create them as needed
%w{ I saw a ghost! }.each { |w| puts w.upcase }
# and from the method side, how to call them
def ok
yield :ok
end
# or pack them into a block to give to someone else
def ok_dokey_ok(&block)
ok(&block)
block[:dokey] # same as block.call(:dokey)
ok(&block)
end
# know where the parentheses go when a method takes arguments and a block.
%w{ a bunch of words }.inject(0) { |size,w| size + 1 } #=> 4
pusher = lambda { |array, word| array.unshift(word) }
%w{ eat more fish }.inject([], &pusher) #=> ['fish', 'more', 'eat' ]
Ответ 2
Этот полностью завершен в основных идиомах Ruby, как в:
-
Поменяйте два значения:
x, y = y, x
-
Параметры, которые, если не указаны, принимают значение по умолчанию
def somemethod(x, y=nil)
-
Выгружает посторонние параметры в массив
def substitute(re, str, *rest)
И так далее...
Ответ 3
Еще несколько идиом:
Использование разделителей %w
, %r
и %(
%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }
Сравнение типов в случае операторов
def something(x)
case x
when Array
# Do something with array
when String
# Do something with string
else
# You should really teach your objects how to 'quack', don't you?
end
end
... и общее злоупотребление методом ===
в операторах case
case x
when 'something concrete' then ...
when SomeClass then ...
when /matches this/ then ...
when (10...20) then ...
when some_condition >= some_value then ...
else ...
end
Что-то, что должно выглядеть естественно для рубистов, но, возможно, не так для людей, пришедших с других языков: использование each
в пользу for .. in
some_iterable_object.each{|item| ... }
В Ruby 1.9+, Rails или путем исправления метода символа # to_proc this становится все более популярным идиомом:
strings.map(&:upcase)
Условный метод/определение константы
SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)
Методы запросов и деструктивные методы (bang)
def is_awesome?
# Return some state of the object, usually a boolean
end
def make_awesome!
# Modify the state of the object
end
Неявные параметры отображения
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
Ответ 4
Мне это нравится:
str = "Something evil this way comes!"
regexp = /(\w[aeiou])/
str[regexp, 1] # <- This
Что (примерно) эквивалентно:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
Или, по крайней мере, то, что я использовал для замены таких блоков.
Ответ 5
Я бы предложил прочитать код популярных и хорошо продуманных плагинов или драгоценных камней от людей, которых вы восхищаетесь и уважаете.
Некоторые примеры, с которыми я столкнулся:
if params[:controller] == 'discussions' or params[:controller] == 'account'
# do something here
end
соответствующего
if ['account', 'discussions'].include? params[:controller]
# do something here
end
который позже будет реорганизован на
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
Ответ 6
Вот несколько, взятых из разных источников:
используйте "except" и "until" вместо "if not" и "while not". Старайтесь не использовать "если", когда существует условие "else".
Помните, что вы можете сразу назначить несколько переменных:
a,b,c = 1,2,3
и даже переменную swap без temp:
a,b = b,a
Используйте подходящие условные обозначения, например
do_something_interesting unless want_to_be_bored?
Помните об обычно используемом, но не сразу очевидном (по крайней мере мне) способе определения методов класса:
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
Некоторые ссылки:
Ответ 7
Кстати, из ссылки вопрос
a ||= b
эквивалентно
if a == nil
a = b
end
Это тонко некорректно и является источником ошибок в приложениях Ruby для новичков.
Поскольку оба (и только) nil
и false
оцениваются как логическое false, a ||= b
фактически (почти *) эквивалентно:
if a == nil || a == false
a = b
end
Или, чтобы переписать его с другой идиомой Ruby:
a = b unless a
(* Поскольку каждый оператор имеет значение, это не технически эквивалентно a ||= b
. Но если вы не полагаетесь на значение инструкции, вы не увидите разницы.)
Ответ 8
Я поддерживаю страницу wiki, которая охватывает некоторые идиомы Ruby и форматирование:
https://github.com/tokland/tokland/wiki/RubyIdioms
Ответ 9
Вы можете легко копировать объект Marshaling. - взято с языка программирования Ruby
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
Обратите внимание, что файлы и потоки ввода-вывода, как а также объекты метода и связывания, слишком динамичны для маршалинга; там не было бы надежным способом восстановления их состояние.
Ответ 10
a = (b && b.attribute) || "default"
примерно:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
Я использую это, когда b - запись, которая может быть или не быть найдена, и мне нужно получить один из ее атрибутов.
Ответ 11
Я всегда забываю точный синтаксис этого сокращения, если оператор else (и имя оператора. комментирует кто-нибудь?) Я думаю, что он широко используется за пределами ruby, но в случае, если кто-то хочет синтаксис здесь:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
расширяется до
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
Обновление
называется тернарным оператором:
return myvar? myvar.size: 0
Ответ 12
Мне нравится, как if-then-elses или case-when могут быть сокращены, потому что они возвращают значение:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
может быть перезаписано
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
То же самое можно применить к case-when:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
Ответ 13
Array.pack и String.unpack для работы с двоичными файлами:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
Ответ 14
не хватает магии
class Dummy
def method_missing(m, *args, &block)
"You just called method with name #{m} and arguments- #{args}"
end
end
Dummy.new.anything(10, 20)
=> "You just called method with name anything and arguments- [10, 20]"
если вы вызываете методы, которые не существуют в ruby-объектах, ruby-интерпретатор будет вызывать метод, называемый method_missing, если он определен, вы можете использовать это для некоторых трюков, например, для написания api wrappers или dsl, где вы не знаете все имена методов и параметров
Ответ 15
Хороший вопрос!
Как я считаю, чем более интуитивно понятный и быстрый код, тем лучше строилось программное обеспечение. Я покажу вам, как я выражаю свои мысли с помощью Ruby в небольших фрагментах кода. Подробнее здесь
Карта
Мы можем использовать метод отображения по-разному:
user_ids = users.map { |user| user.id }
Или:
user_ids = users.map(&:id)
Пример
Мы можем использовать метод rand:
[1, 2, 3][rand(3)]
В случайном порядке:
[1, 2, 3].shuffle.first
И идиоматический, простой и простой способ... образец!
[1, 2, 3].sample
Двойные трубы/мемонирование
Как вы сказали в описании, мы можем использовать memoization:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
Статический метод/метод класса
Мне нравится использовать методы класса, я считаю, что это действительно идиоматический способ создания и использования классов:
GetSearchResult.call(params)
Simple. Красивый. Интуитивно. Что происходит в фоновом режиме?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
Для получения дополнительной информации, чтобы написать идиоматический код Ruby, прочитайте здесь