Question
Une des choses que j'aime à propos de ruby ??est qu'il s'agit principalement d'un langage très lisible (excellent pour le code auto-documenté)
Cependant, inspiré par cette question: code Ruby expliqué
et la description de la façon dont || =
fonctionne en ruby, je pensais aux idiomes de ruby ??que je n'utilise pas, car, franchement, je ne les aime pas complètement.
Ma question est donc, semblable à l'exemple de la question référencée, quels idiomes de ruby ??communs, mais non évidents, dois-je connaître pour être un programmeur de ruby ??vraiment compétent?
Au fait, à partir de la question référencée
a ||= b
est équivalent à
if a == nil || a == false
a = b
end
(Merci à Ian Terrell pour la correction)
Modifier: Il s'avère que ce point n'est pas totalement controversé. Le bon développement est en fait
(a || (a = (b)))
Voir ces liens pour savoir pourquoi:
- http: //DABlog.RubyPAL. Com / 2008/3/25 / a-short-circuit-edge-case /
- http://DABlog.RubyPAL.Com/ 2008/3/26 / court-circuit-post-correction /
- http://ProcNew.Com/ruby-short-circuit -edge-case-response.html
Merci à J & # 246; rg W Mittag pour l'avoir signalé.
La solution
La clause magic if qui permet au même fichier de servir de bibliothèque ou de 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
Emballage et déballage des tableaux:
this(:is => :the, :same => :as)
this({:is => :the, :same => :as})
Le sucre syntatical pour les hachages en tant qu'arguments de méthode
# 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]}
Initialiseurs de hachage:
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
syntaxe de la métaclasse
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 d'instance de 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' ]
Blocs, procs et lambdas. Vivez et respirez.
<*>Autres conseils
Ce diaporama est assez complet sur les principaux idiomes de Ruby, comme dans:
-
Échangez deux valeurs:
x, y = y, x
-
Paramètres qui, s'ils ne sont pas spécifiés, prennent une valeur par défaut
def somemethod (x, y = nil)
-
Combine les paramètres superflus dans un tableau
def substitute (re, str, * rest)
Et ainsi de suite ...
Quelques autres idiomes:
Utilisation des % w
, des % r
et des % (
délimiteurs
%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }
Comparaison de types dans les instructions 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
... et abus général de la méthode ===
dans les instructions 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
Quelque chose qui devrait sembler naturel aux rubyistes, mais peut-être pas aux personnes venant d'autres langues: l'utilisation de chaque
en faveur de pour .. dans
some_iterable_object.each{|item| ... }
Dans Ruby 1.9+, Rails ou en appliquant un correctif à la méthode Symbol # to_proc, ceci devient un idiome de plus en plus populaire:
strings.map(&:upcase)
Définition de la méthode / constante conditionnelle
SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)
Méthodes de requête et méthodes destructives (bang)
def is_awesome?
# Return some state of the object, usually a boolean
end
def make_awesome!
# Modify the state of the object
end
Paramètres de splat implicites
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
J'aime ça:
str = "Something evil this way comes!"
regexp = /(\w[aeiou])/
str[regexp, 1] # <- This
Ce qui équivaut (approximativement) à:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
Ou du moins c'est ce que j'ai utilisé pour remplacer ces blocs.
Je suggérerais de lire le code de plugins populaires et bien conçus ou de trésors de personnes que vous admirez et respectez.
Quelques exemples rencontrés:
if params[:controller] == 'discussions' or params[:controller] == 'account'
# do something here
end
correspondant à
if ['account', 'discussions'].include? params[:controller]
# do something here
end
qui sera plus tard refactored à
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
En voici quelques exemples, issus de diverses sources:
utilisez " à moins que " et "jusqu'à" au lieu de "si pas". et "sans être". Essayez de ne pas utiliser " à moins que " quand un " else " condition existe, bien que.
N'oubliez pas que vous pouvez affecter plusieurs variables à la fois:
a,b,c = 1,2,3
et même permuter la variable sans temp:
a,b = b,a
Utilisez des conditions de fin, le cas échéant, par exemple
.do_something_interesting unless want_to_be_bored?
Soyez conscient d'une façon couramment utilisée (mais du moins pas évidente pour moi) de définir les méthodes de classe:
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
Quelques références:
Au fait, à partir du référencé question
a ||= b
est équivalent à
if a == nil a = b end
C'est subtilement incorrect et constitue une source de bugs dans les applications Ruby des nouveaux arrivants.
Etant donné que (et seulement) nil
et false
sont évalués à un booléen false, a || = b
est en réalité (presque *) équivalent à:
if a == nil || a == false
a = b
end
Ou, pour le réécrire avec un autre idiome Ruby:
a = b unless a
(* Comme chaque instruction a une valeur, techniquement, cela n'est pas équivalent à a || = b
. Mais si vous ne vous fiez pas à la valeur de l'instruction, vous ne verrez pas une différence.)
Je gère une page de wiki qui couvre certains idiomes et formats de Ruby:
J'oublie toujours la syntaxe exacte de ce raccourci si la commande else (et le nom de l'opérateur. commente quelqu'un?) Je pense qu'elle est largement utilisée en dehors de ruby, mais si quelqu'un d'autre veut la syntaxe, voici:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
se développe en
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
mise à jour
appelé l'opérateur ternaire:
retourner myvar? myvar.size: 0
Vous pouvez facilement copier en profondeur avec un objet Marshaling. - extrait du langage de programmation Ruby
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
Notez que les fichiers et les flux d’E / S, comme ainsi que des objets Method et Binding, sont trop dynamiques pour être marshalés; Là serait pas un moyen fiable pour restaurer leur état.
a = (b && b.attribute) || "default"
est à peu près:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
J'utilise ceci lorsque b est un enregistrement qui peut avoir été trouvé ou non, et je dois obtenir l'un de ses attributs.
J'aime le fait que les options If-then-else ou case-when puissent être abrégées car elles renvoient une valeur:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
pourrait être réécrit
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
La même chose pourrait s'appliquer à la casse quand:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
Array.pack et String.unpack pour l'utilisation de fichiers binaires:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
méthode manquante 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]"
si vous appelez des méthodes qui n'existent pas dans les objets ruby, l'interpréteur ruby ??appellera une méthode appelée 'method_missing' si sa définition est définie, vous pouvez l'utiliser pour quelques astuces, comme écrire des wrappers api, ou dsl, où vous ne savez pas tout. méthodes et noms de paramètres
Belle question!
Comme je pense, le plus intuitif & amp; plus le code est rapide, meilleur logiciel que nous construisons. Je vais vous montrer comment exprimer mes pensées en utilisant Ruby dans de petits extraits de code. Lire la suite
Carte
Nous pouvons utiliser la méthode map de différentes manières:
user_ids = users.map { |user| user.id }
Ou:
user_ids = users.map(&:id)
Échantillon
Nous pouvons utiliser la méthode rand:
[1, 2, 3][rand(3)]
Aléatoire:
[1, 2, 3].shuffle.first
Et le moyen idiomatique, simple et le plus simple ... un échantillon!
[1, 2, 3].sample
Double pipe équivalent / mémorisation
Comme vous l'avez dit dans la description, nous pouvons utiliser la mémorisation:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
Méthode statique / Méthode de classe
J'aime utiliser des méthodes de classe, je pense que c'est une façon très idiomatique de créer & amp; utiliser des classes:
GetSearchResult.call(params)
Simple. Belle. Intuitif. Que se passe-t-il en arrière-plan?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
Pour plus d'informations sur l'écriture de code Ruby idiomatique, lisez ici