2010-01-15 3 views
7

Dans SO question 2068165 une réponse a soulevé l'idée d'utiliser quelque chose comme ceci:Est-ce une utilisation raisonnable pour && = dans Ruby?

params[:task][:completed_at] &&= Time.parse(params[:task][:completed_at]) 

comme un moyen sécherie de dire

params[:task][:completed_at] = Time.parse(params[:task][:completed_at]) if params[:task][:completed_at] 

où le Hash params serait provenant d'un formulaire (Rails/ActionView) .

C'est une sorte de corollaire à l'idiome bien connu ||=, définissant la valeur si le LHS est pas nul/faux.

Est-ce que l'utilisation de &&= comme ceci est en fait un idiome de Ruby reconnu que j'ai raté ou ai-je oublié un idiome plus couramment utilisé? Il est devenant plutôt en retard ...

+0

L'analyseur fait des trucs bizarres: a = 1, si une usine ** ** et b = 1 si jamais ** échoue ** –

+0

pour ajouter l'insulte à la blessure si xxx; xxx = 1; fin ** échoue ** –

Répondre

5

Il devrait être. Si rien d'autre, params[:task] est seulement évaluée une fois en utilisant le formulaire &&=.

Pour clarifier:

params[:task][:completed_at] = params[:task][:completed_at] && ... 

appels [](:task) sur params deux fois, et [](:completed_at)[]=(:completed_at) une fois chacun sur params[:task].

params[:task][:completed_at] &&= ... 

appelle [](:task) sur params une fois, et sa valeur est planqué tant pour les appels [](:completed_at) et []=(:completed_at).


Exemple concret qui décrit ce que je suis en train d'illustrer (basé sur le code de l'exemple de Marc-André, merci beaucoup):

class X 
    def get 
    puts "get" 
    @hash ||= {} 
    end 
end 

irb(main):008:0> x = X.new 
=> #<X:0x7f43c496b130> 
irb(main):009:0> x.get 
get 
=> {} 
irb(main):010:0> x.get[:foo] = 'foo' 
get 
=> "foo" 
irb(main):011:0> x.get[:foo] 
get 
=> "foo" 
irb(main):012:0> x.get[:foo] &&= 'bar' 
get 
=> "bar" 
irb(main):013:0> x.get[:foo] = x.get[:foo] && 'bar' 
get 
get 
=> "bar" 

Notez que l'utilisation de la forme « élargie » provoque « get » à être imprimé deux fois, mais en utilisant la forme compacte, il n'est imprimé qu'une seule fois.

+0

Votre réponse est incorrecte. Les deux formes sont équivalentes et params [: task] [: completed_at] est évalué deux fois (en supposant qu'il est vrai) (voir ma réponse). –

+0

Je pense que je dois clarifier exactement ce que je réponds. :-P –

+0

Je souhaite que vous ayez raison, mais non, le getter [: task] sera également exécuté autant de fois dans la forme && = que le formulaire 'if'. J'ai mis à jour ma réponse pour le montrer. –

1

En utilisant &&=, dans le cas de LHS est faux, il est seulement lu une fois, mais n'étant pas défini. Cela devrait le rendre plus clair ...

class Test 
    def initialize(value) 
    @v = value 
    end 
    def v=(value) 
    puts "set" 
    @v = value 
    end 
    def v 
    puts "get=>#{@v}" 
    @v 
    end 
end 
t = Test.new(true) 

t.v = t.v && true 
puts '----' 

t.v &&= true 
puts '----' 

t = Test.new(false) # lets make LHS false 
t.v = t.v && true 
puts '----' 

t = Test.new(false) # lets make LHS false 
t.v &&= true 

Le résultat:

get=>true 
set 
---- 
get=>true 
set 
---- 
get=>false 
set 
---- 
get=>false 
+0

Lorsque LHS est faux, c'est bien: nous ne voulons pas le définir s'il n'est pas là, mais nous pouvons avoir besoin de le convertir/valider/analyser s'il est présent mais pas sous la forme que nous voulons vraiment, par exemple. où nous obtenons une date/heure sous la forme d'une chaîne et que nous voulons la transmettre à un modèle dans le type attendu. –