Общие Ruby Идиомы
одна вещь, которую я люблю в ruby, заключается в том, что в основном это очень читаемый язык (который отлично подходит для самостоятельного документирования кода)
однако, вдохновленный этим вопросом:Ruby код объяснил
и описание как ||=
работает в ruby, я думал о рубиновых идиомах, которые я не использую, так как, честно говоря, я не полностью их Грок.
Итак, мой вопрос, похожий на пример из упомянутого вопроса, Какие общие, но не очевидные, Ruby идиомы мне нужны чтобы быть в курсе, чтобы быть действительно опытным программистом ruby?
кстати, из ссылочного вопроса
a ||= b
эквивалентно
if a == nil || a == false
a = b
end
(спасибо Яну Терреллу за исправление)
Edit: оказывается, этот момент не совсем бесспорен. Правильное расширение на самом деле
(a || (a = (b)))
видеть эти ссылки для почему:
- http://DABlog.RubyPAL.Com/2008/3/25/a-short-circuit-edge-case/
- http://DABlog.RubyPAL.Com/2008/3/26/short-circuit-post-correction/
- http://ProcNew.Com/ruby-short-circuit-edge-case-response.html
спасибо Йоргу W Mittag за указание на это.
15 ответов
предложение magic if, которое позволяет одному и тому же файлу служить библиотекой или скриптом:
if __FILE__ ==
# 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' ]
в syntatical сахар для хэшей в качестве аргументов метода
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"
блоки, процесс и лямбда. Живи и дыши ими.
# 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' ]
этой слайдшоу довольно полно на основных идиомах Ruby, как в:
-
поменять местами два значения:
x, y = y, x
-
параметры, которые, если не указаны, принимают значение по умолчанию
def somemethod(x, y=nil)
-
Пакетирует посторонние параметры в массив
def substitute(re, str, *rest)
и так далее...
еще несколько идиом:
использование %w
, %r
и %(
разделители
%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }
введите сравнение в операторах case
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 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+, рельсы, или исправление символа#to_proc метод,этой становится все более популярной идиомой:
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
неявные параметры splat
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
мне нравится это:
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?
или по крайней мере, это то, что я использовал на замену таких блоков.
Я бы предложил прочитать код популярных и хорошо разработанных плагинов или драгоценных камней от людей, которыми вы восхищаетесь и уважаете.
некоторые примеры, с которыми я столкнулся:
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
вот несколько, отобранных из различных источников:
используйте "если" и " пока "вместо" Если нет "и"пока нет". Однако старайтесь не использовать "если", когда существует условие "еще".
помните, что вы можете назначить несколько переменных сразу:
a,b,c = 1,2,3
и даже переменная подкачки без 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
ссылки:
кстати, из ссылки вопрос
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
. Но если вы не полагаетесь на значение утверждения, вы не увидите разницы.)
вы можете deepcopy с объектом маршалинга легко. - взято из языка программирования Ruby
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
обратите внимание, что файлы и потоки ввода-вывода, Как а также способ и связующие объекты, слишком динамичны для маршалинга; там не было бы надежного способа восстановить свое государство.
a = (b && b.attribute) || "default"
грубо:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
Я использую это, когда b-запись, которая может быть найдена или не найдена, и мне нужно получить один из ее атрибутов.
Я всегда забываю точный синтаксис этого сокращенного оператора if 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
обновление
вызывается тернарный оператор:
вернуть myvar ? аргумент myVar.размер : 0
мне нравится, как If-then-elses или case-when могут быть сокращены, потому что они возвращают значение:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
может быть rewriten
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
массив.рюкзак и веревка.распаковать для работы с двоичными файлами:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
метод отсутствует магия
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 или dsl, где вы не знаете всех имен методов и параметров
хороший вопрос!
как я думаю, чем интуитивно понятнее и быстрее код, тем лучшее программное обеспечение мы строим. Я покажу вам, как я выражаю свои мысли, используя Ruby в небольших фрагментах кода. подробнее здесь
карта
мы можем использовать метод map по-разному:
user_ids = users.map { |user| user.id }
или:
user_ids = users.map(&:id)
пример
мы можем использовать rand метод:
[1, 2, 3][rand(3)]
Shuffle:
[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)
простой. Красивый. Интуитивный. Что происходит на заднем плане?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
для получения дополнительной информации для написания идиоматического кода Ruby прочитайте здесь