読者です 読者をやめる 読者になる 読者になる

せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】パーフェクトRuby 学習感想文 ~余談:ブロック関数とは?理解から自作まで

 
15章のブロック変数の使い方が良く分からない、って書いてたら
師匠からメールが来た

OptionParserがブロックを使う理由はわからんけど、コメント。

例えば、ファイル操作を考えてみよう
Rubyのファイル操作は大きくわけて、2つの書き方がある


f = open("hoge.txt")
puts f.read

f.close

と

open("hoge.txt") do |f|
  puts f.read
end

これはまったく同じ処理になる。

この2つのコードの大きな違いは、f.close やね。
ブロック渡し版は、close書いてないね。
ブロックで渡すと自動でcloseしてくれるから不要なんす。

こんな風にブロックで渡すと、そのブロックの利用はopenメソッドの自由になるから
ブロック実行の前後に好きな処理を挟むとかってことが可能になる。
おそらく、こういう理由やないかな。

opt =  OptionParser.new
opt.on_head ・・・

みたいに書かせてもいいけど、ただ、なんかの後処理があって、その後処理を自動でやるために
ブロックで受け取ってんやないかな



ちなみに、もしかしたら勘違いしてるかもやけど
 
open("hoge.txt") do |f|
  puts f.read
end
のf.readはいつ実行されるかって話ね。


ブロックは引数です。
で、Rubyの場合、引数はメソッドが実行される前に評価(実行)される。
ということは、これはopenの呼び出し前に実行されるもの?
つまり、open メソッドを実行する前に、f.readが実行される?

。。。。というのは間違い。
正解は、open呼び出し中に実行される。
openメソッド内部でf.readが実行されることになります。


つまり、ブロックはその場で実行されるわけじゃなく
ブロックを受け取ったメソッド内で実行されるもの
です。
この点は大事なので、きをつけて。

 
師匠!いつもありがとう!
 
というより、初めてPCからメールくれたね!
内心、師匠・・・Gmailの返信方法が分からないのかなって心配してた。
 
良かった!

 
では、長いから分けて考えよう

Fileのopen

ブロックで渡すと自動でcloseしてくれるから不要なんす。

これは本にも書いてあった。P196だ。

公式ガイドを見てみよう。

ブロックを指定して呼び出した場合は、File オブジェクトを引数として ブロックを実行します。ブロックの実行が終了すると、ファイルは自動的に クローズされます。

http://doc.ruby-lang.org/ja/1.9.3/method/File/s/open.html

これ「知っていないと分からない」系だなあ。


 

OptionParser.new

このコードに関して↓

cp = OptionParser.new do |opt|
    opt.on_head('-v','--version', 'show ver') do |v|
        opt.version = TodoSamp::VERSION
        puts opt.ver + "dayo"
        exit
     end
  end		
cp.parse!(argv)	

こういうコメント↓

(OptionParser.newは)なんかの後処理があって、その後処理を自動でやるために
ブロックで受け取ってんやないかな

そうなのかなー。
OptionParserインスタンスについて調べてみよう。
 
公式のサンプルコード

require 'optparse'
opt = OptionParser.new

opt.on('-a') {|v| p v }
opt.on('-b') {|v| p v }

opt.parse!(ARGV)
p ARGV

ruby sample.rb -a foo bar -b baz
# => true
true
["foo", "bar", "baz"]

http://doc.ruby-lang.org/ja/1.8.7/library/optparse.html

なるほど。

  • OptionParser.newは、ブロックで書かなくていいぽい。
  • 「なんかの後処理」は公式には載ってなかった。

つまり、
OptionParser.newにブロック引数を渡しているのは筆者の趣味ぽい。


 

オプションの登録はブロックじゃないとだめ。

さっきの公式サンプルコード↓

opt = OptionParser.new
opt.on('-a') {|v| p v }

公式の解説↓

1.OptionParser オブジェクト opt を生成する。
2.オプションを取り扱うブロックを opt に登録する。

ふーん。

opt.on_head('-v','--version', 'show ver') do |v|

って、ブロックで登録しないといけないんだ。


ん?
15章のこのコードをもう一度みてみよう

cp = OptionParser.new do |opt|
		opt.on_head('-v','--version', 'show ver') do |v|
			opt.version = TodoSamp::VERSION
			puts opt.ver + "dayo"
			exit
		end
		
	cp.parse!(argv)	
	end

1個目の|opt|はただの趣味だってことが分かったけれど、
2個目の|v|って、こうやってブロック関数でしか表現できないのでは。

on_headメソッドで、
「-v」って引数がきた時の動きは「vブロック」の通りですー、
って事を登録しているのか。
実行できる文、をそのまま渡しているのか。

文を渡すことが大事だから、
「v」っていう変数を文中で使う/使わないが本質ではないんだ。

「渡している」のだから、ブロックも、on_headメソッドの引数の1つなんだ。

2個目のブロックは、ブロックであって当然のところなんだ。
だから師匠も、2個目のブロックについての言及はなかったのか。


先に進もう。

 

ブロックの前後に好きな処理を挟む?

こんな風にブロックで渡すと、そのブロックの利用はopenメソッドの自由になるから
ブロック実行の前後に好きな処理を挟むとかってことが可能になる。

「openメソッドの自由になる」?どういう意味?



あ! なるほど!

opt.on_head('-v','--version', 'show ver') do |v|
			opt.version = TodoSamp::VERSION
			puts opt.ver + "dayo"
			exit
		end

このコードを見ているときに、
on_headメソッドに「文を渡している」「ブロックも引数」っていうことが理解出来た。

「openメソッドの自由になる」っていうのは
openメソッドは、ブロックで渡された文を、
どのタイミングで実行してもいい(しなくてもいい)
って事だ。

だから、

ブロック実行の前後に好きな処理を挟むとかってことが可能になる

んだ。

なるほど!

少し疑問だったのは
なんで file = File.openのときはclose処理をしてくれないんだろう。
ケチだから? と思っていたんだけれど、

f = open("hoge.txt")
puts f.read
f.close

と

open("hoge.txt") do |f|
  puts f.read
end
  • 上のコード⇒ openした後、ファイルに対する処理がいつまで続くか分からない。
  • 下のコード⇒ openした後の処理は全部引数として渡される。

だから、
引数で渡された文を実行した後は ファイルをcloseするって処理を追加できるんだ。

これが、

ブロックの前後に好きな処理を挟む

ってことか!


 

実際にブロックを渡されるメソッドを書いてみる

P96を参考に。

def hoge
	p "aaa"
	yield
	p "ccc"
end

hoge do
	p "bbb"
end

■実行結果

"aaa"
"bbb"
"ccc"


なるほど。
引数として文が渡されるだけだから、
aaa ⇒ bbb の順に出力されるのか。
aaa の前にbbb が表示されることはないんだ。

ブロックはその場で実行されるわけじゃなく
ブロックを受け取ったメソッド内で実行されるもの
です。

って、こういうことか。

この点は大事なので、きをつけて。

はーい。


長くなるので続く。
次は、実際にブロックを自作してみるよ。
続きはこちら