doブロックと中かっこ{}の使用


112

ルビーの初心者は、初心者用の手袋を着用してください。

次の2つのスニペットの間に(あいまいまたは実用的な)違いはありますか?

my_array = [:uno, :dos, :tres]
my_array.each { |item| 
    puts item
}

my_array = [:uno, :dos, :tres]
my_array.each do |item| 
    puts item
end

ブレース構文を使用すると、ブロックを1行に配置できます

my_array.each { |item| puts item }

しかし、それ以外に、ある構文を他の構文よりも使用する説得力のある理由はありますか?


5
すばらしい質問です。経験豊富なルビイストが好むものにも興味があります。
ジョナサンスターリング




1
doブロックの1つのライナーを書くこともできますが、これはeval( "my_array.each do | item |; puts item; end")のような場合にのみ非常に役立ちますが、evalや引用符なしでirbまたはpryで機能します。それが好まれる状況に遭遇するかもしれません。いつでも聞かないで。これも調査すべきもう1つのトピックです。
ダグラスG.アレン

回答:


100

Rubyクックブックによると、ブラケット構文の優先順位はdo..end

ブラケット構文は、do..end構文よりも優先されることに注意してください。次の2つのコードスニペットを考えてみます。

1.upto 3 do |x|
  puts x
end

1.upto 3 { |x| puts x }
# SyntaxError: compile error

2番目の例は、括弧が使用されている場合にのみ機能します。 1.upto(3) { |x| puts x }


7
ああ、わかった。したがって、優先順位が高いため、使用する場合はブロックを追加パラメーターとして渡しますが、角かっこを使用する場合は、メソッド呼び出しの結果の最初のパラメーターとしてブロックを渡します。左側。
アランストーム

2
私はしばしば短い回答を好みますが、bkdirの回答ははるかに明確です。
yakout

70

これは少し古い質問ですが、私はについてもう少し説明してみたい{}do .. end

前に言ったように

ブラケット構文は、do..endよりも優先順位が高い

しかし、これがどのように違いをもたらすか:

method1 method2 do
  puts "hi"
end

この場合、method1はのブロックでdo..end呼び出され、method2は引数としてmethod1に渡されます。これはmethod1(method2){ puts "hi" }

しかし、あなたが言うなら

method1 method2{
  puts "hi"
}

次にmethod2がブロックで呼び出され、戻り値がmethod1に引数として渡されます。これはmethod1(method2 do puts "hi" end)

def method1(var)
    puts "inside method1"
    puts "method1 arg = #{var}"
    if block_given?
        puts "Block passed to method1"
        yield "method1 block is running"
    else
        puts "No block passed to method1"
    end
end

def method2
    puts"inside method2"
    if block_given?
        puts "Block passed to method2"
        return yield("method2 block is running")
    else
        puts "no block passed to method2"
        return "method2 returned without block"
    end
end

#### test ####

method1 method2 do 
    |x| puts x
end

method1 method2{ 
    |x| puts x
}

####出力####

#inside method2
#no block passed to method2
#inside method1
#method1 arg = method2 returned without block
#Block passed to method1
#method1 block is running

#inside method2
#Block passed to method2
#method2 block is running
#inside method1
#method1 arg = 
#No block passed to method1

39

通常、規則は{}、メソッド呼び出しや比較などの小さな操作を行うときに使用するため、これは完全に理にかなっています。

some_collection.each { |element| puts element }

しかし、複数行に渡る少し複雑なロジックがある場合は、次のdo .. endように使用します。

1.upto(10) do |x|
  add_some_num = x + rand(10)
  puts '*' * add_some_num
end

基本的には、ブロックロジックが複数行になり、同じ行に適合できないdo .. end場合に使用します。ブロックロジックが単純で、コードの単純な1行だけの場合はを使用します{}


6
複数行のブロックにdo / endを使用することに同意しますが、ブロックの最後に追加のメソッドをチェーンする場合は、中括弧を使用します。文体的には、do ... end.method()。methodよりも{...}。method()。method()の方が好きですが、それは私だけかもしれません。
Tin Man、

1
同意しますが、ブロック付きのメソッドの結果を意味のある変数に割り当ててから、別のメソッドを呼び出すようresult_with_some_condition = method{|c| c.do_something || whateever}; result_with_some_condition.another_methodにします。しかし、一般的に私は電車の事故を避けます。
nas

なんでこのスタイルが人気なのかしら。「いつもこうやって」以外に理由はありますか?
iGEL

9

選択するための2つの一般的なスタイルがありますdo end{ }Rubyでのブロックのためには:

最初の非常に一般的なスタイルはRuby on Railsによって一般化され、単一行と複数行の単純なルールに基づいています。

  • { }単一行ブロックには中括弧を使用します
  • do end複数行ブロックに使用

これは理にかなっています。do/ endは1ライナーでひどく読み取りますが、複数行ブロックの場合、}それ自体の行にぶら下がっているままにすることはend、モジュール、クラス、メソッド定義などのルビで使用する他のすべてのものと矛盾します(defなど) 。)と制御構造(ifwhilecase、等)

あまり頻繁に見られない2番目のスタイルは、セマンティック、または「ワイリッヒブレース」として知られています。

  • do end手続き型ブロックに使用
  • { }機能ブロックに中括弧を使用する

これは、ブロックがその戻り値について評価されるとき、それは{}連鎖可能であるべきであり、中括弧はメソッド連鎖にとってより意味があることを意味します。

一方、その副作用についてブロックが評価される場合、戻り値は重要ではなく、ブロックは何かを「実行」しているだけなので、連鎖しても意味がありません。

構文のこの違いは、ブロックの評価に関する視覚的な意味と、その戻り値を気にする必要があるかどうかを伝えます。

たとえば、ここではブロックの戻り値がすべてのアイテムに適用されます。

items.map { |i| i.upcase }

ただし、ここではブロックの戻り値を使用していません。それは手続き的に動作しおり、それを使って副作用があります:

items.each do |item|
  puts item
end

セマンティックスタイルのもう1つの利点は、ブロックに行が追加されたからといって、中括弧をdo / endに変更する必要がないことです。

観察として、偶然に機能するブロックはしばしば1行であり、手続き型ブロック(configなど)は複数行です。したがって、Weirichスタイルに従うと、Railsスタイルとほとんど同じように見えます。


1

私は何年もウェイリッヒスタイルを使用していましたが、常にブレースを使用するようにこれから離れました。私はこれまでにブロックスタイルの情報を使用したことを覚えていません。定義は漠然としています。例えば:

date = Timecop.freeze(1.year.ago) { format_date(Time.now) }
customer = Timecop.freeze(1.year.ago) { create(:customer) }

これらは手続き的または機能的ですか?

そして、行数のことは私の意見では役に立たないだけです。1行以上あるかどうかはわかりますが、行を追加または削除しただけでスタイルを変更する必要があるのはなぜですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.