せかいや

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

【Ruby】ブロックの左側には何が来れる?

結論

  • ブロックはEnumeratorクラスのレシーバーが実行できる。
  • Enumeratorクラスのインスタンスは、Enumerable#each で作成できる
  • yield を返すメソッドもブロックを実行できる

※なんて表現したら良いのか分からなく、
タイトルが変な推理小説みたいになってしまった。

そしていきなり結論。

よかった。推理小説じゃなくて。

 

事の起こり

こんなコードを書いたらエラーになった。

["jjj","ooo"] do |tango|
	p tango
end

hoge7.rb:2: syntax error, unexpected keyword_do_block, expecting end-of-input
そもそも構文エラーとして認識されている。


 
正解はこう↓

["jjj","ooo"].each do |tango|
	p tango
end

でも何で?

まって!
「せかいさんは初心者だから」って理由になってないよ!
それ以外の理由ね


 

Array の先祖

p ["jjj","ooo"].class.ancestors
 ⇒[Array, Enumerable, Object, Kernel, BasicObject]


 

Array#each のクラス

p ["jjj","ooo"].each
 ⇒#<Enumerator: ["jjj", "ooo"]:each>


フムフム。Enumeratorクラスだからdoメソッドを実行できるのか。


 

Enumerableを継承していないクラス

これで1文字づつ取れるのかと思ったけどエラー。↓

"str".each do |ss|
	p ss
end

理由:StringクラスはEnumerableクラスを継承していないから。

p "str".class.ancestors
 ⇒[String, Comparable, Object, Kernel, BasicObject]

一文字ずつ取れるのかと思った。なんとなく。


 

yield を返すメソッドもブロックを実行できる

def hoge
	yield "hoge"
end

hoge do |st|
	p st   ←"hoge"が表示
end

 
hoge って何者なんだろうと思って

def hoge
	yield "hoge"
end
p hoge

だとエラー。
`hoge': no block given (yield) (LocalJumpError)

なんだろう。

とりあえず、do の前はyieldを返すメソッドもいけるんだなー。


 

do はメソッドじゃない

do って何なの?って思って。

Enumeratorクラスにはdo というメソッドはない

p Enumerator.instance_methods
p ["hoge", "fuga"].each.methods
[:each, :each_with_index, :each_with_object, :with_index, :with_object, :next_values, :peek_values, :next, :peek, :feed, :rewind, :inspect, :size, :to_a, :entries, :sort, :sort_by, :grep, :count, :find, :detect, :find_index, :find_all, :select, :reject, :collect, :map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :first, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :include?, :reverse_each, :each_entry, :each_slice, :each_cons, :zip, :take, :take_while, :drop, :drop_while, :cycle, :chunk, :slice_before, :lazy, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
[:each, :each_with_index, :each_with_object, :with_index, :with_object, :next_values, :peek_values, :next, :peek, :feed, :rewind, :inspect, :size, :to_a, :entries, :sort, :sort_by, :grep, :count, :find, :detect, :find_index, :find_all, :select, :reject, :collect, :map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :first, :all?, :any?, :one?, :none?, :min, :max, :minmax, :min_by, :max_by, :minmax_by, :member?, :include?, :reverse_each, :each_entry, :each_slice, :each_cons, :zip, :take, :take_while, :drop, :drop_while, :cycle, :chunk, :slice_before, :lazy, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

うーん? そうか。do ってメソッドじゃないのか。

そういえばdo って、

catch :entire do
	catch :process do
		throw :entire
	end
end

みたいにcatch 文でも書いてる。
条件分岐とかでも書いてる。

このcatch文でのdo は このときのレシーバーが実行しているとすれば、
うーん?Objectクラスでdoメソッドが定義されているのかも?

Object Kernel のメソッド一覧

[:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
[:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for]

ないかー。当然かー。
Objectクラスで定義されていたら全クラスで見えるんだから、なくて当たり前か。

あー。
do って if とか case とかend みたいな、メソッドじゃない何かなのか。
なんていうんだろう?言い方が分からないけれど。

と、大きくわき道にそれたので、結論を頭に書いておきました。


ブロック、まだまだ奥が深い。