【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 みたいな、メソッドじゃない何かなのか。
なんていうんだろう?言い方が分からないけれど。
と、大きくわき道にそれたので、結論を頭に書いておきました。
ブロック、まだまだ奥が深い。