faire..end vs accolades bouclés pour les blocs en Ruby

J'ai un collègue qui essaie activement de me convaincre que je ne devrais pas utiliser do..terminez et utilisez plutôt des accolades pour définir des blocs multilignes dans Ruby.

Je suis fermement dans le camp de n'utiliser que des accolades pour les courts one-liners et do..fin pour tout le reste. Mais je pensais que je voudrais tendre la main à la grande communauté pour obtenir une résolution.

Alors, qui est-ce, et pourquoi? (Exemple de code shoulda)

context do
  setup { do_some_setup() }
  should "do somthing" do
    # some more code...
  end
end

Ou

context {
  setup { do_some_setup() }
  should("do somthing") {
    # some more code...
  }
}

Personnellement, il suffit de regarder le ci-dessus répond à la question pour moi, mais je voulais ouvrir cela à la plus grande communauté.

130
demandé sur chollida 2011-04-08 00:32:52

13 réponses

La convention générale est d'utiliser faire..fin pour les blocs multi-lignes et accolades pour les blocs à une seule ligne, mais il y a aussi une différence entre les deux qui peut être illustrée avec cet exemple:

puts [1,2,3].map{ |k| k+1 }
2
3
4
=> nil
puts [1,2,3].map do |k| k+1; end
#<Enumerator:0x0000010a06d140>
=> nil

Cela signifie que {} a une priorité plus élevée que do..fin, donc gardez cela à l'esprit lorsque vous décidez ce que vous voulez utiliser.

P. S: un autre exemple à garder à l'esprit pendant que vous développez vos préférences.

Le code suivant:

task :rake => pre_rake_task do
  something
end

Vraiment moyens:

task(:rake => pre_rake_task){ something }

Et ce code:

task :rake => pre_rake_task {
  something
}

Signifie Vraiment:

task :rake => (pre_rake_task { something })

Donc, pour obtenir la définition réelle que vous voulez, avec des accolades, vous devez faire:

task(:rake => pre_rake_task) {
  something
}

Peut-être que l'utilisation d'accolades pour les paramètres est quelque chose que vous voulez faire de toute façon, mais si vous ne le faites pas, il est probablement préférable d'utiliser do..fin dans ces cas pour éviter cette confusion.

218
répondu Pan Thomakos 2011-04-07 20:57:46

À Partir De Programmation Ruby:

Les accolades ont une priorité élevée; do a une priorité faible. Si l'invocation de la méthode a des paramètres qui ne sont pas entre parenthèses, la forme d'Accolade d'un bloc se liera au dernier paramètre, pas à l'invocation globale. La forme do se liera à l'invocation.

Donc le code

f param {do_something()}

Lie le bloc à la variable param tandis que le code

f param do do_something() end

Lie le bloc à la fonction f.

Cependant, ce n'est pas un problème si vous mettez des arguments de fonction entre parenthèses.

46
répondu David Brown 2011-04-07 20:43:24

Il y a quelques points de vue à ce sujet, c'est vraiment une question de préférence personnelle. Beaucoup de rubyists prennent l'approche que vous faites. Cependant, deux autres styles qui sont communes à l'est de toujours utiliser l'un ou l'autre, ou d'utiliser {} pour les blocs de valeurs de retour, et do ... end pour les blocs qui sont exécutées pour des effets secondaires.

14
répondu GSto 2011-04-07 20:38:51

Il y a un avantage majeur à accolades - de nombreux éditeurs ont un temps beaucoup plus facile de les faire correspondre, ce qui rend certains types de débogage beaucoup plus facile. Pendant ce temps, le mot-clé " faire...end " est un peu plus difficile à faire correspondre, d'autant plus que "end"correspond également à "if".

8
répondu GlyphGryph 2011-11-09 20:41:17

Je vote pour do / end


La convention est do .. end pour multiline et { ... } Pour one-liners.

, Mais j'aime do .. end mieux, donc quand j'ai une seule ligne, j'utilise do .. end de toute façon, mais en forme comme d'habitude pour faire/fin en trois lignes. Cela rend tout le monde heureux.

  10.times do 
    puts ...
  end

Un problème avec { } est que c'est la poésie-mode-hostile (parce qu'ils se lient étroitement au dernier paramètre et non à l'appel de méthode entier, donc vous devez inclure la méthode parens) et ils juste, à mon avis, ne regardez pas aussi beau. Ils ne sont pas des groupes d'instructions et ils entrent en conflit avec les constantes de hachage pour la lisibilité.

De plus, j'ai vu assez de { } dans les programmes C. Le chemin de Ruby, comme d'habitude, est meilleur. Il y a exactement un type de bloc if, et vous n'avez jamais à revenir en arrière et à convertir une instruction en une instruction composée.

7
répondu DigitalRoss 2011-04-07 23:08:34

La règle la plus courante que j'ai vue (plus récemment dans Eloquent Ruby ) est:

  • S'il s'agit d'un bloc multiligne, utilisez do / end
  • S'il s'agit d'un bloc à une seule ligne, utilisez {}
6
répondu Peter Brown 2011-04-07 20:42:31

Quelques rubyistes influents suggèrent d'utiliser des accolades lorsque vous utilisez la valeur de retour,et do / end lorsque vous ne le faites pas.

Http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace (sur archive.org)

Http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (sur archive.org)

Cela semble être une bonne pratique en général.

Je modifierais un peu ce principe pour dire que vous devriez éviter d'utiliser do/end sur une seule ligne car c'est plus difficile à lire.

Vous devez être plus prudent en utilisant des accolades car il se liera au paramètre final d'une méthode au lieu de l'appel de la méthode entière. Juste ajouter des parenthèses pour éviter cela.

5
répondu Kelvin 2017-03-27 21:00:01

Se résume à un biais personnel, je préfère les accolades sur un bloc do/end car il est plus compréhensible pour un plus grand nombre de développeurs en raison de la majorité des langages de fond les utilisent sur la Convention do/end. Cela étant dit, la vraie clé est de parvenir à un accord au sein de votre boutique, si do/end est utilisé par les développeurs 6/10 que tout le monde devrait les utiliser, si 6/10 utilise des accolades, puis s'en tenir à ce paradigme.

Il s'agit de créer un modèle pour que l'équipe dans son ensemble puisse identifiez les structures de code plus rapidement.

1
répondu Jake Kalstad 2011-04-07 20:37:58

Il y a une différence subtile entre eux, mais { } se lie plus étroitement que do / end.

1
répondu Shankar Raju 2011-04-07 20:38:51

En fait, c'est une préférence personnelle, mais cela dit, pour les 3 dernières années de mes expériences ruby, ce que j'ai appris, c'est que ruby a son style.

Un exemple serait, si vous venez d'un arrière-plan JAVA, pour une méthode booléenne que vous pouvez utiliser

def isExpired
  #some code
end 

Notez le cas de chameau et le plus souvent le préfixe 'est' pour l'identifier comme une méthode booléenne.

Mais dans ruby world, la même méthode serait

def expired?
  #code
end

Donc je pense personnellement, il vaut mieux aller avec 'Ruby way' (Mais je sais qu'il faut du temps pour comprendre (il m'a fallu environ 1 an :D)).

Enfin, j'irais avec

do 
  #code
end

Blocs.

1
répondu sameera207 2011-04-10 22:46:47

J'ai mis une autre réponse, bien que la différenceGrande ait déjà été soulignée (prcedence/binding), et cela peut causer des problèmes difficiles à trouver(L'homme de fer, et d'autres l'ont souligné). Je pense que mon exemple montre le problème avec un extrait de code pas si habituel, même les programmeurs expérimentés ne lisent pas comme le sunday times:

module I18n
    extend Module.new {
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    }
end

module InplaceTrans
    extend Module.new {
        def translate(old_translate, *args)
            Translator.new.translate(old_translate, *args)
        end
    }
end

Ensuite, j'ai fait un peu d'embellissement de code ...

#this code is wrong!
#just made it 'better looking'
module I18n
    extend Module.new do
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end
end

Si vous modifiez le {} ici do/end, vous obtiendrez l'erreur, que la méthode translate ne ne pas exister ...

Pourquoi cela se produit est souligné ici plus d'un-priorité. Mais où mettre des accolades ici? (@ The Tin Man: j'utilise toujours des accolades, comme vous, mais ici ... supervisé)

Donc chaque réponse comme

If it's a multi-line block, use do/end
If it's a single line block, use {}

Est juste faux {[11] } s'il est utilisé sans le "mais gardez un œil sur les accolades / priorité!"

Encore:

extend Module.new {} evolves to extend(Module.new {})

Et

extend Module.new do/end evolves to extend(Module.new) do/end

(ce que le résultat de extend fait avec le bloc ...)

Donc, si vous voulez utiliser faites / utilisez ceci:

#this code is ok!
#just made it 'better looking'?
module I18n
    extend(Module.new do 
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end)
end
1
répondu halfbit 2014-07-11 10:40:29

Mon style personnel est de mettre l'accent sur la lisibilité sur les règles rigides de {...} vs do...end choix, quand un tel choix est possible. Mon idée de la lisibilité est la suivante:

[ 1, 2, 3 ].map { |e| e + 1 }      # preferred
[ 1, 2, 3 ].map do |e| e + 1 end   # acceptable

[ 1, 2, 3 ].each_with_object [] do |e, o| o << e + 1 end # preferred, reads like a sentence
[ 1, 2, 3 ].each_with_object( [] ) { |e, o| o << e + 1 } # parens make it less readable

Foo = Module.new do     # preferred for a multiline block, other things being equal
  include Comparable
end

Foo = Module.new {      # less preferred
  include Comparable
}

Foo = Module.new { include Comparable }      # preferred for a oneliner
Foo = module.new do include Comparable end   # imo less readable for a oneliner

[ [ 1 ], [ 1, 2 ] ].map { |e| e.map do |e| e + 1 end }  # slightly better
[ [ 1 ], [ 1, 2 ] ].map { |e| e.map { |e| e + 1 } }     # slightly worse

Dans une syntaxe plus complexe, telle que des blocs imbriqués multilignes, j'essaie d'intercaler {...} et do...end délimiteurs pour la plupart des résultats naturels, par exemple.

Foo = Module.new { 
  if true then
    Bar = Module.new {                          # I intersperse {} and keyword delimiters
      def quux
        "quux".tap do |string|                  # I choose not to intersperse here, because
          puts "(#{string.size} characters)"    # for multiline tap, do ... end in this
        end                                     # case still loks more readable to me.
      end
    }
  end
}

Bien que l'absence de règles rigides puisse produire des choix légèrement différents pour différents programmeurs, je crois que cas par cas l'optimisation de la lisibilité, bien que subjective, est un gain net par rapport au respect de règles rigides.

1
répondu Boris Stitnicky 2014-08-03 14:25:47

Il y a une troisième option: Écrire un préprocesseur pour déduire " end " sur sa propre ligne, à partir de l'indentation. Les penseurs profonds qui préfèrent le code concis ont raison.

Mieux encore, pirater ruby donc c'est un drapeau.

Bien sûr, la solution la plus simple est d'adopter la convention de style selon laquelle une séquence de extrémités apparaît sur la même ligne, et d'apprendre à votre coloration syntaxique à les couper. Pour faciliter l'édition, on pourrait utiliser des scripts d'éditeur pour développer / réduire ces séquence.

20% à 25% de mes lignes de code ruby sont "end" sur leur propre ligne, toutes inférées trivialement par mes conventions d'indentation. Ruby est le langage lisp à avoir obtenu le plus grand succès. Quand les gens contestent cela, demandant où sont toutes les parenthèses horribles, je leur montre une fonction ruby se terminant par sept lignes de "fin" superflue.

J'ai codé pendant des années en utilisant un préprocesseur lisp pour déduire la plupart des parenthèses: une barre ' | ' a ouvert un groupe autoclosé à la fin de la ligne, et un signe dollar ' $ ' a servi d'espace réservé vide où il n'y aurait autrement aucun symbole pour aider à déduire les groupements. C'est bien sûr un territoire de guerre de religion. Lisp / scheme sans les parenthèses est le plus poétique de toutes les langues. Pourtant, il est plus facile de simplement calmer les parenthèses en utilisant la coloration syntaxique.

Je code toujours avec un préprocesseur pour Haskell, pour ajouter des heredocs, et pour par défaut toutes les lignes de vidage en tant que Commentaires, tout est indenté en tant que code. Je n'aime pas les caractères de commentaire, quelle que soit la langue.

-1
répondu Syzygies 2017-10-29 22:06:08