ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例

[Ruby - Nokogiriによるスクレイピング(YahooFinance) - Qiita](http://qiita.com/amatsu1108/items/5f0825249182dbfb9e8d#comment-88380c6e40e499451a34)
のコメント欄を書いているときに、挙動の違いに改めて気付いたので挙げときます。
Enumerable#injectに限られる話ではないのですが、気付いたときに使っていたのがこのメソッドだったので。
##先に結論を
**do…endで書くときには、返値確認用のpを直接使うな。**
##Enumerable#inject
まず中括弧の場合。injectの例としてこちらから引用します。
[ruby の inject をわかりやすく説明してみる - Λάδι Βιώσας](http://kenkiti.hatenadiary.jp/entry/20090114/ruby_inject)

```sample1
pry(main)> p (1..10).inject(0) {|sum, i| sum + i }
55
=> 55
```
初期値0をsumに入れて、返値sum + iをsumに戻して次のループへ、ってメソッドですね。
ここで使っている中括弧を単純にdo…endに変えます。

```sample2
pry(main)> p (1..10).inject(0) do |sum, i|
pry(main)*   sum + i  
pry(main)* end  
TypeError: 0 is not a symbol
from (pry):**:in `inject'
```
…え？
そうだよ「０はシンボルじゃない」よ。当然じゃないか。
なんでそんなエラーを返すの？？
###るりまを確認
>{ ... } の方が do ... end ブロックよりも強く結合します。次に例を挙げますが、このような違いが影響するコードは読み辛いので避けましょう:
>
```
foobar a, b do .. end   # foobarの引数はa, bの値とブロック
foobar a, b { .. }      # ブロックはメソッドbの引数、aの値とbの返り値とがfoobarの引数
```
[メソッド呼び出し(super・ブロック付き・yield)](http://rurema.clear-code.com/1.9.3/doc/spec=2fcall.html)

つまり、括弧を明示すると

```
foobar(a, b) do .. end   # foobarの引数はa, bの値とブロック
foobar(a, b { .. })      # ブロックはメソッドbの引数、aの値とbの返り値とがfoobarの引数
```
とRubyインタプリタは解釈します。

###考察
pを使うときには一般的に、引数を囲む括弧を用いない。
確認のために括弧を明示してみる。

```ruby:括弧無し
p (1..10).inject(0) do |sum, i|
  sum + i
end
```
上記のことを考慮すると

```ruby:括弧付き
p((1..10).inject(0)) do |sum, i|
  sum + i
end
```
と解釈されてそう。

```sample3
pry(main)> p((1..10).inject(0)) do |sum, i|
pry(main)*   sum + i  
pry(main)* end  
TypeError: 0 is not a symbol
from (pry):**:in `inject'

pry(main)> p((1..10).inject(0))
TypeError: 0 is not a symbol
from (pry):67:in `inject'
```
ビンゴ。
###るりまのEnumerable#injectを確認
>
```
inject(init = self.first) {|result, item| ... } -> object[permalink]
inject(sym) -> object
```
[PARAM] sym:
ブロックの代わりに使われるメソッド名を表す Symbol オブジェクトを指定します。 実行結果に対して sym という名前のメソッドが呼ばれます。
[instance method Enumerable#inject](http://rurema.clear-code.com/1.9.3/method/Enumerable/i/inject.html)

###理解
1. do…endだと{…}よりも結合度が低いので、pを使うと`(1..10).inject(0)`までがpの引数として扱われた。
1. そのためにinjectの引数が「Symbolじゃない」とエラーが出た。
1. うしろに付いているdo…endブロックは無視されている。

##解決例
```ruby:例
result = (1..10).inject(0) do |sum, i|
  sum + i
end
p result
```

##まとめ
大事なことなので二度書きます。
**do…endで書くときには、返値確認用のpを直接使うな。**

##はてブコメントへの返答
>###id: mattn
まとめが逆な気がした。「pを使う時は do end は使うな」？

Kernel.#pは基本的にデバッグ用のメソッドです。
つまり、ここでpを使ったのは *返値を一時的に知りたいから* であり、最終的にpは削除します。
という心持ちでこのエントリを書いたので、do…endが主体になっています。
また、「直接」と書いたのは **返値を知りたいならば、一時変数に代入してからpで表示すべきであり、一時変数を省いてpを使うと痛い目に遭う** という意味合いでした（分かりづらかったので「解決例」の段落を追加しました。

>###id: ngsw
http://www.oki-osk.jp/esc/ruby/tut-07.html ここらと同じなのだろうか

ご指摘の通りです。
上記サイト
[Ruby チュートリアル - 7. 微妙な問題](http://www.oki-osk.jp/esc/ruby/tut-07.html)
から一部を引用します。
>7.5 do … end ブロックの優先順位

>```
p [1,2,3].each {|x| puts x}, 4  # 無事に実行される
p [1,2,3].each do |x| puts x end, 4  # SyntaxError
```
…カンマが後続することは構文的にあり得ないから…

>```
p [1,2,3].each do |x| puts x end  # LocalJumpError
```
なぜだろうか？…つまり，括弧で明示すると

>```
p([1,2,3].each()) {|x| puts x}  # LocalJumpError
```
であるかのように構文解析される。 たしかに SyntaxError ではないが，イテレータである each にブロックが渡されないから， LocalJumpError が発生する。

まさにdo…endと{…}との挙動の違いです。

>###id: otchy210
Rubyist では無いので詳しくないのだけど、よく色んな言語で見かける、&& と and の強さの違いみたいのが、ブロックでもあるっていう事か。

ご指摘のように、演算子の優先順序に近いものがあると思います。

* Rubyはメソッドの引数を明示する括弧を（意味が不明瞭にならない範囲で）省くことが出来る
* 標準出力へ出力するメソッド（puts, p, pp, print, …）では、括弧を省くのが通常の書き方

この二点が影響して（私的なミスが生じて）今回のエントリを書くことになったわけですw

>###id: tanakaBox
割と色々ある気がする。この場合、`(1..10).inject(0) do |sum, i|; sum + i; end.tap{|t| p t}`と、`end`の後に続ければいいんじゃね？

今回の目的を満足するためには、全くその通りです！知らなかったので感謝です！
気になることとしては、すこし話がずれるのですが、`end.tap`というふうに、endのあとにメソッドを続けることに違和感が生じること、でしょうか。（パーフェクトRuby pp.101-2にも、do…endと{…}の書き分け理由の一つに挙げられてました。

後日追記：
末尾に `Object#tap` メソッドを使わなくても `Object#display` メソッドがあるじゃないか。
[忘れられた出力メソッドObject#display - Qiita](https://qiita.com/riocampos/items/42d55c7a5dfcb69f05e4)