Do..endを使用して、代わりに中括弧を使用してRubyで複数行のブロックを定義しないことを積極的に説得しようとしている同僚がいます。
私は、短いワンライナーには中括弧のみを使用し、他のすべてには使用しないというキャンプにしっかりといます。しかし、解決策を得るために、より大きなコミュニティに手を差し伸べると思いました。
それで、それはなぜですか? (いくつかのshouldaコードの例)
context do
setup { do_some_setup() }
should "do somthing" do
# some more code...
end
end
または
context {
setup { do_some_setup() }
should("do somthing") {
# some more code...
}
}
個人的には、上記を見るだけで質問に答えられますが、これをより大きなコミュニティに開放したかったのです。
一般的な規則は、do..endを複数行ブロックに使用し、中括弧を単一行ブロックに使用することですが、この例で説明できる2つの違いもあります。
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
つまり、{}はdo..endよりも優先順位が高いため、使用するものを決定する際にはそのことに留意してください。
追伸:プリファレンスを作成する際に留意すべきもう1つの例です。
次のコード:
task :rake => pre_rake_task do
something
end
本当に意味します:
task(:rake => pre_rake_task){ something }
そしてこのコード:
task :rake => pre_rake_task {
something
}
本当に意味します:
task :rake => (pre_rake_task { something })
したがって、必要な実際の定義を中括弧で取得するには、次の手順を実行する必要があります。
task(:rake => pre_rake_task) {
something
}
パラメータにブレースを使用することは、とにかくやりたいことですが、そうでない場合は、この混乱を避けるために、do..endを使用することをお勧めします。
プログラミングRuby から:
中括弧は優先順位が高くなります。優先順位は低いです。メソッド呼び出しに括弧で囲まれていないパラメーターがある場合、ブレースのブロック形式は、呼び出し全体ではなく最後のパラメーターにバインドされます。 doフォームは呼び出しにバインドします。
だからコード
f param {do_something()}
コードを実行中にブロックをparam
変数にバインドします
f param do do_something() end
ブロックを関数f
にバインドします。
ただし、関数の引数を括弧で囲む場合、これは問題ではありません。
これにはいくつかの観点がありますが、実際には個人的な好みの問題です。多くのルビイストはあなたがするアプローチを取ります。ただし、一般的な他の2つのスタイルは、常にどちらか一方を使用するか、{}
値を返すブロックの場合、do ... end
副作用のために実行されるブロック用。
中かっこには大きな利点が1つあります。多くの編集者は、はるかに簡単に一致するため、特定の種類のデバッグがはるかに簡単になります。一方、キーワード「do ... end」は、特に「end」も「if」と一致するため、一致がかなり難しくなります。
私が見た最も一般的なルールは(最近では Eloquent Ruby で):
規則は、マルチラインの場合はdo .. end
、ワンライナーの場合は{ ... }
です。
しかし、私はdo .. end
のほうが好きなので、ライナーが1つあるときは、do .. end
を使用しますが、do/endは通常どおり3行でフォーマットします。これは誰もが幸せになります。
10.times do
puts ...
end
{ }
の1つの問題は、それが詩モードの敵対的であることです(メソッド呼び出し全体ではなく最後のパラメーターにしっかりとバインドするため、メソッドの括弧を含める必要があります)。ニースのように見えます。それらはステートメントグループではなく、読みやすさのためにハッシュ定数と衝突します。
さらに、Cプログラムで{ }
を十分に見ました。 Rubyの方法は、いつものように優れています。 if
ブロックには1つのタイプがあり、文を複合文に戻す必要はありません。
影響力のあるルビイストの中には、戻り値を使用する場合は中括弧を使用し、使用しない場合は終了することをお勧めします。
http://talklikeaduck.denhaven2.com/2007/10/02/Ruby-blocks-do-or-brace (archive.org)
http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (archive.org)
これは一般的に良い習慣のようです。
この原則を少し変更して、読みにくいので、単一行でdo/endを使用しないでください。
メソッド呼び出し全体ではなく、メソッドの最後のパラメーターにバインドするため、ブレースの使用にはさらに注意する必要があります。それを避けるために括弧を追加してください。
bigの違いはすでに指摘されていましたが(別の答え)、別の答えを付けました。私の例は、それほど一般的ではないコードスニペットの問題を示していると思います。経験豊富なプログラマでさえ、日曜日のようには読みません。
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
それから私はいくつかのコードを美しくしました...
#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
ここで{}
をdo/end
に変更すると、エラーが発生します。そのメソッドtranslate
は存在しません...
なぜこれが起こるのかは、ここで複数指摘されています-優先順位。しかし、ここにブレースを配置する場所は? (@ the Tin Man:私はいつもあなたのようにブレースを使用していますが、ここでは...監督されています)
だからすべての答えのような
If it's a multi-line block, use do/end If it's a single line block, use {}
「wrong」を「BUTブレース/優先順位に注意してください」なしで使用した場合
再び:
extend Module.new {} evolves to extend(Module.new {})
そして
extend Module.new do/end evolves to extend(Module.new) do/end
(extendの結果がブロックで行うことは...)
したがって、do/endを使用する場合は、これを使用します。
#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
個人的なバイアスになりますが、大多数のバックグラウンド言語がdo/end規約で使用しているため、より多くの開発者にとって理解しやすいため、do/endブロックよりも中括弧を好んで使用します。 6/10の開発者がdo/endを使用する場合、誰もが使用するよりも6/10の開発者が使用する場合、6/10が中括弧を使用する場合は、そのパラダイムに固執することが重要です。
チーム全体がコード構造をより迅速に識別できるように、パターンを作成することがすべてです。
それらの間には微妙な違いがありますが、{}はdo/endよりも強くバインドします。
私の個人的なスタイルは、{
...}
vs do
...end
選択(そのような選択が可能な場合)。読みやすさの私の考えは次のとおりです。
[ 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
複数行のネストされたブロックなど、より複雑な構文では、{
...}
およびdo
...end
は、最も自然な結果のための区切り文字です。
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
}
厳格なルールの欠如はプログラマーごとにわずかに異なる選択肢を生み出す可能性がありますが、読みやすさのケースバイケースの最適化は主観的ではありますが、厳格なルールを順守することで得られる純利益だと思います。
実際、それは個人的な好みですが、過去3年間、私のRuby私が学んだことは、Rubyにはスタイルがあるということを経験しています。
1つの例は、Java backgroundから来ている場合、使用するブールメソッドに対して
def isExpired
#some code
end
キャメルケースと、ほとんどの場合、ブールメソッドとして識別するための「is」プレフィックスに注意してください。
しかし、Ruby worldでは、同じメソッドは
def expired?
#code
end
個人的には、「Rubyの方法」を使用する方が良いと思います(しかし、理解するには時間がかかります(約1年かかりました:D))。
最後に、私は
do
#code
end
ブロック。