33 votes

Méthode combinatoire comme tap, mais capable de retourner une valeur différente?

Je traverse une phase où j'essaie d'éviter les variables temporaires et l'abus de conditions là où je peux utiliser un style de codage plus fluide. J'ai beaucoup aimé utiliser #tap dans les cas où je veux obtenir la valeur dont j'ai besoin pour la retourner, mais faire quelque chose avec avant de la retourner.

def méthode_fluide
  quelque_chose_de_compliqué(a, b, c).tap do |obj|
    obj.update(:x => y)
  end

Par rapport à la méthode procédurale:

def méthode_non_fluide
  obj = quelque_chose_de_compliqué(a, b, c)
  obj.update(:x => y)
  obj # <= Je n'aime pas ça, si c'est évitable

De toute évidence, les exemples ci-dessus sont simples, mais c'est un style de codage assez courant dans la communauté ruby. Parfois, j'utiliserai #inject pour passer un objet à travers une série de filtres également:

choses.inject(quelque_chose) do |obj, chose|
  chose.filtre(obj)

Par rapport à la méthode procédurale:

obj = quelque_chose
choses.each do |chose|
  obj = chose.filter(obj)

Maintenant, je suis confronté à une utilisation répétée d'une condition comme celle-ci, et je cherche une approche plus fluide pour la gérer:

def méthode_pas_sympa
  obj = quelque_chose_de_complexe(a, b, c)
  if a_predicate_check?
    obj.one_more_method_call
  else
    obj
  end

La solution (légèrement) plus propre est d'éviter la variable temporaire au détriment de la duplication:

def méthode_pas_sympa
  if a_predicate_check?
    quelque_chose_de_complexe(a, b, c).one_more_method_call
  else
    quelque_chose_de_complexe(a, b, c)
  end

Je ne peux m'empêcher de ressentir le désir d'utiliser quelque chose presque comme #tap ici cependant.

Quels autres motifs pourrais-je suivre ici. Je réalise que tout cela n'est que du sucre absurde pour certaines personnes et que je devrais passer à des problèmes plus intéressants, mais j'essaie d'apprendre à écrire dans un style plus fonctionnel, donc je suis juste curieux de savoir ce que les rubyistes de longue date ont déterminé être de bonnes façons de traiter des situations comme celle-ci. Ces exemples sont très simplifiés.

14voto

tokland Points 29813

Définir Object#as:

class Object
  def as
    yield self
  end
end

Et maintenant vous pouvez écrire :

def not_sure_this_is_nice_enough_method1
  something_complex(a, b, c).as do |obj| 
    a_predicate_check? ? obj.one_more_method_call : obj
  end
end

14voto

kuboon Points 1033
def best_nice_method
  something_complex(a, b, c).tap |obj|
    break obj.one_more_method_call if a_predicate_check?
  end
end

La magie est break dans tap renvoie une autre valeur.

nouveau

ruby 2.5 a yield_self qui est exactement ce que vous voulez. https://stackoverflow.com/a/47890832/683157

8voto

instance_eval peut être mal utilisé à cette fin

"this".instance_eval { |test| test + " works" }

depuis 2.5 il est possible d'utiliser yield_self

"easy".yield_self{ |a| a + " peasy" }

Lire la suite:

https://ruby-doc.org/core-1.9.3/BasicObject.html#method-i-instance_eval

https://ruby-doc.org/core-2.5.0/Object.html#method-i-yield_self

3voto

Tyler Rick Points 3033

J'ai trouvé une méthode dans la gemme Facets qui pourrait être ce que vous cherchiez : Kernel#ergo

Donc votre méthode d'origine :

def not_nice_method
  obj = something_complex(a, b, c)
  if a_predicate_check?
    obj.one_more_method_call
  else
    obj
  end

pourrait ressembler à ceci :

require 'facets/kernel/ergo'

def nice_method
  something_complex(a, b, c).ergo do |_| 
    a_predicate_check? ? _.one_more_method_call : _
  end

1voto

Sterling Paramore Points 143

J'avais besoin de faire quelque chose comme ça et j'aime la réponse de tokland, mais je ne voulais pas polluer Object pour le petit script que j'écrivais. Au lieu de cela, j'ai utilisé tap sur un tableau :

[quelque_chose_de_compliqué].tap { |s| s[0] = nouvelle_chose_cool)}.first

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X