web-dev-qa-db-ja.com

Ruby内のブロックのdo..endと中括弧

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...
  }
}

個人的には、上記を見るだけで質問に答えられますが、これをより大きなコミュニティに開放したかったのです。

136
Blake Taylor

一般的な規則は、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を使用することをお勧めします。

229
Pan Thomakos

プログラミングRuby から:

中括弧は優先順位が高くなります。優先順位は低いです。メソッド呼び出しに括弧で囲まれていないパラメーターがある場合、ブレースのブロック形式は、呼び出し全体ではなく最後のパラメーターにバインドされます。 doフォームは呼び出しにバインドします。

だからコード

f param {do_something()}

コードを実行中にブロックをparam変数にバインドします

f param do do_something() end

ブロックを関数fにバインドします。

ただし、関数の引数を括弧で囲む場合、これは問題ではありません。

50
David Brown

これにはいくつかの観点がありますが、実際には個人的な好みの問題です。多くのルビイストはあなたがするアプローチを取ります。ただし、一般的な他の2つのスタイルは、常にどちらか一方を使用するか、{}値を返すブロックの場合、do ... end副作用のために実行されるブロック用。

14
GSto

中かっこには大きな利点が1つあります。多くの編集者は、はるかに簡単に一致するため、特定の種類のデバッグがはるかに簡単になります。一方、キーワード「do ... end」は、特に「end」も「if」と一致するため、一致がかなり難しくなります。

8
GlyphGryph

私が見た最も一般的なルールは(最近では Eloquent Ruby で):

  • 複数行ブロックの場合は、do/endを使用します
  • 単一行ブロックの場合は、{}を使用します
7
Peter Brown

私はdo/endに投票しています


規則は、マルチラインの場合はdo .. end、ワンライナーの場合は{ ... }です。

しかし、私はdo .. endのほうが好きなので、ライナーが1つあるときは、do .. endを使用しますが、do/endは通常どおり3行でフォーマットします。これは誰もが幸せになります。

  10.times do 
    puts ...
  end

{ }の1つの問題は、それが詩モードの敵対的であることです(メソッド呼び出し全体ではなく最後のパラメーターにしっかりとバインドするため、メソッドの括弧を含める必要があります)。ニースのように見えます。それらはステートメントグループではなく、読みやすさのためにハッシュ定数と衝突します。

さらに、Cプログラムで{ }を十分に見ました。 Rubyの方法は、いつものように優れています。 ifブロックには1つのタイプがあり、文を複合文に戻す必要はありません。

7
DigitalRoss

影響力のあるルビイストの中には、戻り値を使用する場合は中括弧を使用し、使用しない場合は終了することをお勧めします。

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を使用しないでください。

メソッド呼び出し全体ではなく、メソッドの最後のパラメーターにバインドするため、ブレースの使用にはさらに注意する必要があります。それを避けるために括弧を追加してください。

5
Kelvin

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
1
halfbit

個人的なバイアスになりますが、大多数のバックグラウンド言語がdo/end規約で使用しているため、より多くの開発者にとって理解しやすいため、do/endブロックよりも中括弧を好んで使用します。 6/10の開発者がdo/endを使用する場合、誰もが使用するよりも6/10の開発者が使用する場合、6/10が中括弧を使用する場合は、そのパラダイムに固執することが重要です。

チーム全体がコード構造をより迅速に識別できるように、パターンを作成することがすべてです。

1
Jake Kalstad

それらの間には微妙な違いがありますが、{}はdo/endよりも強くバインドします。

1
Shankar Raju

私の個人的なスタイルは、{...} 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
}

厳格なルールの欠如はプログラマーごとにわずかに異なる選択肢を生み出す可能性がありますが、読みやすさのケースバイケースの最適化は主観的ではありますが、厳格なルールを順守することで得られる純利益だと思います。

1
Boris Stitnicky

実際、それは個人的な好みですが、過去3年間、私のRuby私が学んだことは、Rubyにはスタイルがあるということを経験しています。

1つの例は、Java backgroundから来ている場合、使用するブールメソッドに対して

def isExpired
  #some code
end 

キャメルケースと、ほとんどの場合、ブールメソッドとして識別するための「is」プレフィックスに注意してください。

しかし、Ruby worldでは、同じメソッドは

def expired?
  #code
end

個人的には、「Rubyの方法」を使用する方が良いと思います(しかし、理解するには時間がかかります(約1年かかりました:D))。

最後に、私は

do 
  #code
end

ブロック。

1
sameera207