Pregunta
Una cosa que me encanta de ruby ??es que, en su mayoría, es un lenguaje muy legible (lo cual es excelente para el código de auto-documentación)
Sin embargo, inspirado por esta pregunta: Explicación del Código Ruby
y la descripción de cómo || =
funciona en ruby, estaba pensando en los modismos ruby ??que no uso, ya que francamente no los asimilo por completo.
Entonces, mi pregunta es, similar al ejemplo de la pregunta a la que se hace referencia, ¿qué expresiones de rubí comunes, pero no obvias, debo tener en cuenta para ser un programador de rubíes verdaderamente competente?
Por cierto, de la pregunta referenciada
a ||= b
es equivalente a
if a == nil || a == false
a = b
end
(Gracias a Ian Terrell por la corrección)
Editar: Resulta que este punto no es totalmente indiscutible. De hecho, la expansión correcta es
(a || (a = (b)))
Vea estos enlaces para saber por qué:
- http: //DABlog.RubyPAL. Com / 2008/3/25 / a-short-circuit-edge-case /
- http://DABlog.RubyPAL.Com/ 2008/3/26 / short-circuit-post-correct /
- http://ProcNew.Com/ruby-short-circuit -edge-case-response.html
Gracias a J & # 246; rg W Mittag por señalar esto.
Solución
La cláusula magic if que permite que el mismo archivo sirva como biblioteca o script:
if __FILE__ == # 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 library may be run as a standalone script
end
Empaquetado y desempaquetado de matrices:
this(:is => :the, :same => :as)
this({:is => :the, :same => :as})
El azúcar sintético para los hashes como argumentos de método
# 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]}
Inicializadores de hash:
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
sintaxis de metaclase
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"
variables de instancia de clase
# 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' ]
Bloques, procs y lambdas. Vive y respira.
<*>Otros consejos
Esta presentación de diapositivas es bastante completa en los idiomas principales de Ruby, como en:
-
Intercambia dos valores:
x, y = y, x
-
Parámetros que, si no se especifican, adquieren algún valor predeterminado
def algún método (x, y = nil)
-
Combina los parámetros extraños en una matriz
def sustituto (re, str, * rest)
Y así sucesivamente ...
Algunos modismos más:
Uso de los % w
, % r
y % (
delimitadores
%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }
Comparación de tipos en declaraciones de casos
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
... y abuso general del método ===
en las declaraciones de casos
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
Algo que debería parecer natural para los rubistas, pero tal vez no para las personas que vienen de otros idiomas: el uso de cada
a favor de para .. en
some_iterable_object.each{|item| ... }
En Ruby 1.9+, Rails, o parcheando el método Symbol # to_proc, esto se está convirtiendo en un idioma cada vez más popular:
strings.map(&:upcase)
Método condicional / definición constante
SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)
Métodos de consulta y métodos destructivos (explosión)
def is_awesome?
# Return some state of the object, usually a boolean
end
def make_awesome!
# Modify the state of the object
end
Parámetros implícitos de splat
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
Me gusta esto:
str = "Something evil this way comes!"
regexp = /(\w[aeiou])/
str[regexp, 1] # <- This
Lo que equivale (aproximadamente) a:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
O al menos eso es lo que he usado para reemplazar tales bloques.
Sugeriría leer el código de complementos o gemas populares y bien diseñados de personas que admiras y respetas.
Algunos ejemplos que he encontrado:
if params[:controller] == 'discussions' or params[:controller] == 'account'
# do something here
end
correspondiente a
if ['account', 'discussions'].include? params[:controller]
# do something here
end
que luego será refactorizado a
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
Aquí hay algunos, seleccionados de varias fuentes:
utilice " a menos que " y "hasta" en lugar de " si no " y " mientras que no " ;. Trate de no usar " a menos que " cuando un " else " la condición existe, sin embargo.
Recuerda que puedes asignar múltiples variables a la vez:
a,b,c = 1,2,3
e incluso cambiar la variable sin una temperatura:
a,b = b,a
Use condicionales finales cuando sea apropiado, por ejemplo,
do_something_interesting unless want_to_be_bored?
Tenga en cuenta una forma de definición de métodos de clase comúnmente utilizada pero no instantáneamente obvia (al menos para mí):
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
Algunas referencias:
Por cierto, de la referencia pregunta
a ||= b
es equivalente a
if a == nil a = b end
Eso es sutilmente incorrecto, y es una fuente de errores en las aplicaciones Ruby de los recién llegados.
Dado que ambos (y solo) nil
y false
se evalúan como booleanos falsos, a || = b
es en realidad (casi *) equivalente a:
if a == nil || a == false
a = b
end
O, para reescribirlo con otro lenguaje Ruby:
a = b unless a
(* Como cada declaración tiene un valor, estos no son técnicamente equivalentes a a || = b
. Pero si no confías en el valor de la declaración, no verás una diferencia.)
Mantengo una página wiki que cubre algunos modismos y formatos de Ruby:
Siempre olvido la sintaxis exacta de esta abreviatura en caso contrario (y el nombre del operador. ¿alguien comenta?) Creo que se usa ampliamente fuera de ruby, pero en caso de que alguien más quiera la sintaxis, aquí está:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
se expande a
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
update
llamado operador ternario:
devuelve myvar? myvar.size: 0
Puedes realizar una copia en profundidad con el objeto Marshaling fácilmente. - Tomado del lenguaje de programación Ruby
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
Tenga en cuenta que los archivos y las secuencias de E / S, como así como los objetos Método y Encuadernación, son demasiado dinámicos para ser ordenados; ahí no sería una forma confiable de restaurar su estado.
a = (b && b.attribute) || "default"
es más o menos:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
Utilizo esto cuando b es un registro que puede o no haberse encontrado, y necesito obtener uno de sus atributos.
Me gusta cómo acortar If-then-else o case-when porque devuelven un valor:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
podría reescribirse
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
Lo mismo podría aplicarse al caso cuando:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
Array.pack y String.unpack para trabajar con archivos binarios:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
Magia faltante del método
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]"
si llama a métodos que no existen en los objetos ruby, el intérprete ruby ??llamará a un método llamado 'method_missing' si está definido, puede usar esto para algunos trucos, como escribir envoltorios de api, o dsl, donde no sabe todos métodos y nombres de parámetros
Buena pregunta!
Como creo, el más intuitivo & amp; más rápido es el código, un mejor software que estamos construyendo. Te mostraré cómo expreso mis pensamientos usando Ruby en pequeños fragmentos de código. Lea más aquí
Mapa
Podemos usar el método de mapa de diferentes maneras:
user_ids = users.map { |user| user.id }
O:
user_ids = users.map(&:id)
Muestra
Podemos usar el método rand:
[1, 2, 3][rand(3)]
Barajar:
[1, 2, 3].shuffle.first
Y la forma idiomática, simple y fácil ... muestra!
[1, 2, 3].sample
Double Pipe Equals / Memoization
Como dijiste en la descripción, podemos usar la memoria:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
Método estático / Método de clase
Me gusta usar métodos de clase, creo que es una forma muy idiomática de crear & amp; usar clases:
GetSearchResult.call(params)
Simple. Hermoso. Intuitivo. ¿Qué pasa en el fondo?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
Para obtener más información sobre cómo escribir código Ruby idiomático, lea aquí