Domanda
Una cosa che adoro di ruby ??è che per lo più è un linguaggio molto leggibile (il che è ottimo per il codice auto-documentante)
Tuttavia, ispirato a questa domanda: Spiegazione del codice Ruby
e la descrizione di come || =
funziona in ruby, stavo pensando agli idiomi ruby ??che non uso, francamente non li scrivo completamente.
Quindi la mia domanda è, simile all'esempio della domanda di riferimento, di quali idiomi rubini comuni, ma non ovvi, di cui ho bisogno per essere consapevole di essere un programmatore rubino veramente abile?
A proposito, dalla domanda di riferimento
a ||= b
è equivalente a
if a == nil || a == false
a = b
end
(Grazie a Ian Terrell per la correzione)
Modifica: Si scopre che questo punto non è totalmente controverso. La corretta espansione è infatti
(a || (a = (b)))
Vedi questi link per il motivo:
- http: //DABlog.RubyPAL. com / 2008/3/25 / a-cortocircuito-bordo-case /
- http://DABlog.RubyPAL.Com/ 2008/3 / 26 / cortocircuito post-correzione /
- http://ProcNew.Com/ruby-short-circuit -edge-case-response.html
Grazie a Jörg W Mittag per averlo segnalato.
Soluzione
La clausola if magica che consente di utilizzare lo stesso file come libreria 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
Matrici di compressione e decompressione:
this(:is => :the, :same => :as)
this({:is => :the, :same => :as})
Lo zucchero sintetico per gli hash come argomenti del metodo
# 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]}
Inizializzatori di 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
sintassi della metaclasse
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"
variabili dell'istanza di classe
# 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' ]
Blocks, procs e lambdas. Vivi e respira.
<*>Altri suggerimenti
Questa slideshow è abbastanza completa sui principali idiomi di Ruby, come in:
-
Scambia due valori:
x, y = y, x
-
Parametri che, se non specificati, assumono un valore predefinito
def somemethod (x, y = nil)
-
Esegue il raggruppamento di parametri estranei in un array
def substitute (re, str, * rest)
E così via ...
Alcuni altri modi di dire:
Uso dei delimitatori % w
, % r
e % (
%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }
Confronto dei tipi nelle dichiarazioni dei casi
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
... e l'abuso generale del metodo ===
nelle dichiarazioni dei casi
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
Qualcosa che dovrebbe sembrare naturale per i rubyisti, ma forse non così per le persone che provengono da altre lingue: l'uso di ciascuno
a favore di per .. in
some_iterable_object.each{|item| ... }
In Ruby 1.9+, Rails, o rattoppando il metodo Symbol # to_proc, questo sta diventando un linguaggio sempre più popolare:
strings.map(&:upcase)
Metodo condizionale / definizione costante
SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)
Metodi di query e metodi distruttivi (bang)
def is_awesome?
# Return some state of the object, usually a boolean
end
def make_awesome!
# Modify the state of the object
end
Parametri splat impliciti
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
Mi piace questo:
str = "Something evil this way comes!"
regexp = /(\w[aeiou])/
str[regexp, 1] # <- This
Che è (approssimativamente) equivalente a:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
O almeno è quello che ho usato per sostituire tali blocchi.
Suggerirei di leggere il codice di plugin o gemme popolari e ben progettati da persone che ammiri e rispetti.
Alcuni esempi in cui mi sono imbattuto:
if params[:controller] == 'discussions' or params[:controller] == 'account'
# do something here
end
corrispondente a
if ['account', 'discussions'].include? params[:controller]
# do something here
end
che in seguito verrebbe refactored in
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
Eccone alcuni, scelti da varie fonti:
usa " a meno che " e " fino a " invece di " in caso contrario " e " mentre non & " ;. Cerca di non utilizzare " a meno che " quando un "altro" la condizione esiste, tuttavia.
Ricorda che puoi assegnare più variabili contemporaneamente:
a,b,c = 1,2,3
e persino scambiare la variabile senza temp:
a,b = b,a
Usa i condizionali finali ove appropriato, ad esempio
do_something_interesting unless want_to_be_bored?
Sii consapevole di un modo comunemente usato ma non immediatamente ovvio (almeno per me) di definire i metodi di classe:
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
Alcuni riferimenti:
A proposito, dal referenziato domanda
a ||= b
è equivalente a
if a == nil a = b end
È leggermente errato ed è una fonte di bug nelle applicazioni Ruby dei nuovi arrivati.
Poiché entrambi (e solo) nil
e false
valutano un falso booleano, a || = b
è in realtà (quasi *) equivalente a:
if a == nil || a == false
a = b
end
Oppure, per riscriverlo con un altro idioma di Ruby:
a = b unless a
(* Poiché ogni istruzione ha un valore, questi non sono tecnicamente equivalenti a a || = b
. Ma se non fai affidamento sul valore dell'istruzione, non vedrai una differenza.)
Mantengo una pagina wiki che copre alcuni idiomi e formattazioni di Ruby:
Dimentico sempre l'esatta sintassi di questa istruzione if else (e il nome dell'operatore. commenta qualcuno?) Penso che sia ampiamente usato al di fuori di ruby, ma nel caso in cui qualcun altro voglia la sintassi qui è:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
si espande in
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
update
chiamato l'operatore ternario:
restituisci myvar? myvar.size: 0
Puoi eseguire facilmente la copia in profondità con l'oggetto Marshaling. - tratto da The Ruby Programming Language
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
Si noti che i file e i flussi di I / O, come nonché oggetti Method e Binding, sono troppo dinamici per essere marshaled; Là non sarebbe un modo affidabile per ripristinare il loro stato.
a = (b && b.attribute) || "default"
è approssimativamente:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
Lo uso quando b è un record che potrebbe essere stato trovato o meno e che devo ottenere uno dei suoi attributi.
Mi piace come If-then-else o case-when possano essere abbreviati perché restituiscono un valore:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
potrebbe essere riscritto
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
Lo stesso potrebbe essere applicato a case-when:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
Array.pack e String.unpack per lavorare con file binari:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
metodo mancante magick
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]"
se chiami metodi che non esistono negli oggetti ruby, l'interprete ruby ??chiamerà il metodo chiamato 'method_missing' se è definito, potresti usarlo per alcuni trucchi, come scrivere api wrapper o dsl, dove non conosci; nomi di metodi e parametri
Bella domanda!
Secondo me il più intuitivo & amp; più veloce è il codice, un software migliore che stiamo costruendo. Ti mostrerò come esprimo i miei pensieri usando Ruby in piccoli frammenti di codice. Leggi di più qui
Mappa
Possiamo usare il metodo della mappa in diversi modi:
user_ids = users.map { |user| user.id }
o
user_ids = users.map(&:id)
Esempio
Possiamo usare il metodo rand:
[1, 2, 3][rand(3)]
Shuffle:
[1, 2, 3].shuffle.first
E il modo idiomatico, semplice e più semplice ... prova!
[1, 2, 3].sample
Uguali / memoizzazione doppio tubo
Come hai detto nella descrizione, possiamo usare la memoization:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
Metodo statico / Metodo di classe
Mi piace usare i metodi di classe, penso che sia un modo davvero idiomatico di creare & amp; usa le classi:
GetSearchResult.call(params)
Semplice. Bellissimo. Intuitivo. Cosa succede in background?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
Per maggiori informazioni su come scrivere il codice Ruby idiomatico, leggi qui