■Procクラスのオブジェクト
Proc オブジェクト(手続きオブジェクト)は、ブロックを、コンテキスト(ローカル変数のスコープやスタックフレーム)とともに、オブジェクト化したもの。手続きオブジェクトにはコードのブロックが含まれていると解釈することができる(ような気もする)。
ちなみに、「ブレース { } でくくられたコードの塊(かたまり)」「コードの断片」「コード片」「コードブロック」「ブロック」「doとendの間のコード」は、ほぼ同意味。但し、{ ... } の方が do ... end ブロックよりも強く結合する、など完全同意義というわけではない(場合もあると思われる)。
■ブロックを「Procオブジェクト」にするメリット
ブロックのままだとメソッド内でyieldするしかないが、ブロックをProcオブジェクトにすると、変数に保存したり引数としてメソッドに渡したりすることができるようになり、その結果ブロックの中のコードを好きな時に (何度でも)実行させることが出来るようになる。
↓
他のオブジェクトと同じように保存したり受け渡したりすることが出来るようになる
■「Procオブジェクト」と「メソッド」との違い
メソッドと似ているが、これがオブジェクトに付属しているものではない(それ自身 がオブジェクト)である点が異なる
↓
手続きオブジェクト(proc)はオブジェクトであるが、メソッドはオブジェクトではない
↓
・ひとつのメソッドを他のメソッドに渡すことは出来ない(手続きオブジェクトは可能)
・メソッドは他のメソッドを返すことは出来ない(手続きオブジェクトを返すことは可能)
<HR size="1" color="#7587DD" style="border-style:dashed">
[size=x-large]■ブロックをProcクラスのオブジェクトへと変換する3つの方法[/size]
[size=large][color=004E98]▼1.最後の引数に&が付いたメソッドにブロックを渡す[/color][/size]
メソッド定義の最後の仮引数に&(アンパサンド)から始まる引数が指定されている場合、呼び出し側のメソッドに関連付けられたコードブロックがProcクラスのオブジェクトに変換され、そのオブジェクトがこの最後の仮引数に代入される。この仮引数は普通に変数として扱う事が出来、Proc#callメソッドを呼び出す事でコードブロックを実行する事が出来る。
つまり、メソッド仮引数に&をつけると、その変数は Proc オブジェクトであると解釈される。
ちなみに、ブロック自体はリテラルではないが、Procクラスを利用することで、「手続きオブジェクト」としてオブジェクト化することが出来る。
・仮引数の&xがオブジェクトであることが、見た目上ちょっと判りにくいので……。
def method1( n, &x )
puts x.call(n)
end
method1(2) { |x| x + 1 }
→ 3
method1(2) { |x| x * x }
→ 4
・試しに仮引数の&x表記を&obj0と変更してみたところ、視覚的にここがオブジェクトであることが判るようになったが、その分、メソッドを呼び出すブロックの{ |obj0| obj0 + 1 }という表記に違和感を感じるようになってしまった……。
def method1( n, &obj0 )
puts obj0.call(n)
end
method1(2) { |obj0| obj0 + 1 }
→ 3
method1(2) { |obj0| obj0 * obj0 }
→ 4
・で、結局、こう表記してみることにする
def method1( n, &obj0 ) #2.呼出側メソッドより渡されてきたコードブロック{ |x| x + 1 }が、オブジェクトに変換され、仮引数&obj0に代入される
puts obj0.call(n) #3.オブジェクトとなったコードブロック{ |x| x + 1 }に、引数n(=2)が渡され、実行される。
end
method1(2) { |x| x + 1 } #1.method1メソッドを呼び出す際、引数2とともにコードブロック{ |x| x + 1 }を渡す
→ 3 #4.処理結果
method1(2) { |x| x * x } #5.method1メソッドを呼び出す際、引数2とともにコードブロック{ |x| x * x }を渡す
→ 4 #6.処理結果
・別の例1
def method2(&block) #2.呼出メソッドコードブロック{|x| puts x}が、オブジェクトに変換され、この仮引数に代入される
@block = block #3.オブジェクトとなったコードブロック{|x| puts x}は、インスタント変数@blockに格納される
end
def method3 #5.method3メソッドが呼び出される
@block.call("A0") #6.オブジェクトとなったコードブロック{|x| puts x}に、引数"A0"が渡され、実行される。
end
method2() {|x| puts x} #1.method2メソッドを呼び出す際、コードブロック{|x| puts x}を渡す
method3() #4.method3メソッドを呼び出す
A0 #7.処理の結果がputsにより出力される
・別の例2
obj1 = proc {|i| puts i } #1.ブロック付きKernel.procメソッドによりオブジェクトとなった{|i| puts i }をobj1へ格納
(5..8).each(&obj1) #2.オブジェクトとなった{|i| puts i }が引数で渡され、そのオブジェクトへ対して、順次(5..8).eachが実行される
→ 5
→ 6
→ 7
→ 8
=> 5..8
・別の例3(内容確認中)
def method7
obj7 = Proc.new
obj7.call
end
method7 do
puts 'テスト'
end
→ テスト
def m1
obj7 = proc
obj7.call
end
method7 do
puts 'テスト'
end
→ テスト
[size=large][color=004E98]▼2.ブロック付きでProc.newを呼び出すことにより、Procクラスのオブジェクトへと変換[/color][/size]
▽2-1 引数0のブロックをオブジェクトへ変換(引数の数0)
obj2 = Proc.new{
puts "A2"
puts "A2"
puts "A2"
}
▽2-2 引数1のブロックをオブジェクトへ変換(引数の数1)
obj3 = Proc.new{ |x| x * x }
obj4 = Proc.new do |x|
puts '私は '+x+' が解りません!'
end
▽2_3 引数2のブロックをオブジェクトへ変換(引数の数2)
obj5 = Proc.new{ |x, y| x + y }
obj6 = Proc.new do |x, y|
puts '私は '+x+' と '+y+' が解りません!'
end
[size=large][color=004E98]▼3.ブロック付きでKernel.lambaメソッド(または推奨はされないが同じ機能を持つKernel.procメソッド)を呼び出すことにより、Procクラスのオブジェクトへと変換[/color][/size]
▽3-1 引数0のブロックをオブジェクトへ変換(引数の数0)
obj7 = proc{
puts "A7"
puts "A7"
puts "A7"
}
▽3_2 引数1のブロックをオブジェクトへ変換(引数の数1)
obj8 = proc{ |x| x * x }
obj9 = proc do |x|
puts '私は '+x+' が解りません!'
end
▽3_3 引数2のブロックをオブジェクトへ変換(引数の数2)
obj10 = proc{ |x, y| x + y }
<HR size="1" color="#7587DD" style="border-style:dashed">
[size=x-large]■Proc オブジェクトの呼出し[/size]
[size=large][color=004E98]▼1 call [/color][/size]
▽1-1 引数を渡さない(引数の数0)
obj2.call #obj2オブジェクトへcallメッセージ送信(obj2オブジェクトを呼び出して実行)
→ A2
→ A2
→ A2
▽1-2 引数を1つ渡して呼び出し、実行(引数の数1)
obj3.call(2) #obj3オブジェクトへ、引数を1つ渡して呼び出し、実行
=> 4
obj4.call 'プログラミング'
私は プログラミング が解りません!
▽1-3 引数を2つ渡して呼び出し、実行(引数の数2)
obj5.call(1,2) #obj5オブジェクトへ、引数を2つ渡して呼び出し、実行
=> 3
obj6.call('プログラミング','ネットワーク構築')
私は プログラミング と ネットワーク構築 が解りません!
[size=large][color=004E98]▼2 [] [/color][/size]
▽2-1 引数を1つ渡して呼び出し、実行(引数の数1)
obj4[2]
=> 4
obj4['プログラミング']
私は プログラミング が解りません!
▽2-2 引数を2つ渡して呼び出し、実行(引数の数2)
obj5[1,2]
=> 3
obj6['プログラミング','ネットワーク構築']
私は プログラミング と ネットワーク構築 が解りません!
<HR size="1" color="#7587DD" style="border-style:dashed">
[size=x-large]■to_procメソッド[/size]
&演算子はProcオブジェクトをブロックへ変換し、ブロックをProcオブジェクトへ変換する。
▽より詳細
&をブロックへ変換しようとする。
その際、ブロックへの変換にはRuby組み込みの型変換が利用される。
1.ブロックへの型変換ではまず、Procオブジェクトが与えられているかをチェック。
2.ブロックが与えられていなければ、引数として与えられたオブジェクトをProcへと変換すべく to_procメソッドが呼び出される。このとき、to_proc メソッドが定義されていれば、それが呼び出される。to_procメソッドは、Procオブジェクトを返す。
Proc オブジェクト(手続きオブジェクト)は、ブロックを、コンテキスト(ローカル変数のスコープやスタックフレーム)とともに、オブジェクト化したもの。手続きオブジェクトにはコードのブロックが含まれていると解釈することができる(ような気もする)。
ちなみに、「ブレース { } でくくられたコードの塊(かたまり)」「コードの断片」「コード片」「コードブロック」「ブロック」「doとendの間のコード」は、ほぼ同意味。但し、{ ... } の方が do ... end ブロックよりも強く結合する、など完全同意義というわけではない(場合もあると思われる)。
■ブロックを「Procオブジェクト」にするメリット
ブロックのままだとメソッド内でyieldするしかないが、ブロックをProcオブジェクトにすると、変数に保存したり引数としてメソッドに渡したりすることができるようになり、その結果ブロックの中のコードを好きな時に (何度でも)実行させることが出来るようになる。
↓
他のオブジェクトと同じように保存したり受け渡したりすることが出来るようになる
■「Procオブジェクト」と「メソッド」との違い
メソッドと似ているが、これがオブジェクトに付属しているものではない(それ自身 がオブジェクト)である点が異なる
↓
手続きオブジェクト(proc)はオブジェクトであるが、メソッドはオブジェクトではない
↓
・ひとつのメソッドを他のメソッドに渡すことは出来ない(手続きオブジェクトは可能)
・メソッドは他のメソッドを返すことは出来ない(手続きオブジェクトを返すことは可能)
<HR size="1" color="#7587DD" style="border-style:dashed">
[size=x-large]■ブロックをProcクラスのオブジェクトへと変換する3つの方法[/size]
[size=large][color=004E98]▼1.最後の引数に&が付いたメソッドにブロックを渡す[/color][/size]
メソッド定義の最後の仮引数に&(アンパサンド)から始まる引数が指定されている場合、呼び出し側のメソッドに関連付けられたコードブロックがProcクラスのオブジェクトに変換され、そのオブジェクトがこの最後の仮引数に代入される。この仮引数は普通に変数として扱う事が出来、Proc#callメソッドを呼び出す事でコードブロックを実行する事が出来る。
つまり、メソッド仮引数に&をつけると、その変数は Proc オブジェクトであると解釈される。
ちなみに、ブロック自体はリテラルではないが、Procクラスを利用することで、「手続きオブジェクト」としてオブジェクト化することが出来る。
・仮引数の&xがオブジェクトであることが、見た目上ちょっと判りにくいので……。
def method1( n, &x )
puts x.call(n)
end
method1(2) { |x| x + 1 }
→ 3
method1(2) { |x| x * x }
→ 4
・試しに仮引数の&x表記を&obj0と変更してみたところ、視覚的にここがオブジェクトであることが判るようになったが、その分、メソッドを呼び出すブロックの{ |obj0| obj0 + 1 }という表記に違和感を感じるようになってしまった……。
def method1( n, &obj0 )
puts obj0.call(n)
end
method1(2) { |obj0| obj0 + 1 }
→ 3
method1(2) { |obj0| obj0 * obj0 }
→ 4
・で、結局、こう表記してみることにする
def method1( n, &obj0 ) #2.呼出側メソッドより渡されてきたコードブロック{ |x| x + 1 }が、オブジェクトに変換され、仮引数&obj0に代入される
puts obj0.call(n) #3.オブジェクトとなったコードブロック{ |x| x + 1 }に、引数n(=2)が渡され、実行される。
end
method1(2) { |x| x + 1 } #1.method1メソッドを呼び出す際、引数2とともにコードブロック{ |x| x + 1 }を渡す
→ 3 #4.処理結果
method1(2) { |x| x * x } #5.method1メソッドを呼び出す際、引数2とともにコードブロック{ |x| x * x }を渡す
→ 4 #6.処理結果
・別の例1
def method2(&block) #2.呼出メソッドコードブロック{|x| puts x}が、オブジェクトに変換され、この仮引数に代入される
@block = block #3.オブジェクトとなったコードブロック{|x| puts x}は、インスタント変数@blockに格納される
end
def method3 #5.method3メソッドが呼び出される
@block.call("A0") #6.オブジェクトとなったコードブロック{|x| puts x}に、引数"A0"が渡され、実行される。
end
method2() {|x| puts x} #1.method2メソッドを呼び出す際、コードブロック{|x| puts x}を渡す
method3() #4.method3メソッドを呼び出す
A0 #7.処理の結果がputsにより出力される
・別の例2
obj1 = proc {|i| puts i } #1.ブロック付きKernel.procメソッドによりオブジェクトとなった{|i| puts i }をobj1へ格納
(5..8).each(&obj1) #2.オブジェクトとなった{|i| puts i }が引数で渡され、そのオブジェクトへ対して、順次(5..8).eachが実行される
→ 5
→ 6
→ 7
→ 8
=> 5..8
・別の例3(内容確認中)
def method7
obj7 = Proc.new
obj7.call
end
method7 do
puts 'テスト'
end
→ テスト
def m1
obj7 = proc
obj7.call
end
method7 do
puts 'テスト'
end
→ テスト
[size=large][color=004E98]▼2.ブロック付きでProc.newを呼び出すことにより、Procクラスのオブジェクトへと変換[/color][/size]
▽2-1 引数0のブロックをオブジェクトへ変換(引数の数0)
obj2 = Proc.new{
puts "A2"
puts "A2"
puts "A2"
}
▽2-2 引数1のブロックをオブジェクトへ変換(引数の数1)
obj3 = Proc.new{ |x| x * x }
obj4 = Proc.new do |x|
puts '私は '+x+' が解りません!'
end
▽2_3 引数2のブロックをオブジェクトへ変換(引数の数2)
obj5 = Proc.new{ |x, y| x + y }
obj6 = Proc.new do |x, y|
puts '私は '+x+' と '+y+' が解りません!'
end
[size=large][color=004E98]▼3.ブロック付きでKernel.lambaメソッド(または推奨はされないが同じ機能を持つKernel.procメソッド)を呼び出すことにより、Procクラスのオブジェクトへと変換[/color][/size]
▽3-1 引数0のブロックをオブジェクトへ変換(引数の数0)
obj7 = proc{
puts "A7"
puts "A7"
puts "A7"
}
▽3_2 引数1のブロックをオブジェクトへ変換(引数の数1)
obj8 = proc{ |x| x * x }
obj9 = proc do |x|
puts '私は '+x+' が解りません!'
end
▽3_3 引数2のブロックをオブジェクトへ変換(引数の数2)
obj10 = proc{ |x, y| x + y }
<HR size="1" color="#7587DD" style="border-style:dashed">
[size=x-large]■Proc オブジェクトの呼出し[/size]
[size=large][color=004E98]▼1 call [/color][/size]
▽1-1 引数を渡さない(引数の数0)
obj2.call #obj2オブジェクトへcallメッセージ送信(obj2オブジェクトを呼び出して実行)
→ A2
→ A2
→ A2
▽1-2 引数を1つ渡して呼び出し、実行(引数の数1)
obj3.call(2) #obj3オブジェクトへ、引数を1つ渡して呼び出し、実行
=> 4
obj4.call 'プログラミング'
私は プログラミング が解りません!
▽1-3 引数を2つ渡して呼び出し、実行(引数の数2)
obj5.call(1,2) #obj5オブジェクトへ、引数を2つ渡して呼び出し、実行
=> 3
obj6.call('プログラミング','ネットワーク構築')
私は プログラミング と ネットワーク構築 が解りません!
[size=large][color=004E98]▼2 [] [/color][/size]
▽2-1 引数を1つ渡して呼び出し、実行(引数の数1)
obj4[2]
=> 4
obj4['プログラミング']
私は プログラミング が解りません!
▽2-2 引数を2つ渡して呼び出し、実行(引数の数2)
obj5[1,2]
=> 3
obj6['プログラミング','ネットワーク構築']
私は プログラミング と ネットワーク構築 が解りません!
<HR size="1" color="#7587DD" style="border-style:dashed">
[size=x-large]■to_procメソッド[/size]
&演算子はProcオブジェクトをブロックへ変換し、ブロックをProcオブジェクトへ変換する。
▽より詳細
&をブロックへ変換しようとする。
その際、ブロックへの変換にはRuby組み込みの型変換が利用される。
1.ブロックへの型変換ではまず、Procオブジェクトが与えられているかをチェック。
2.ブロックが与えられていなければ、引数として与えられたオブジェクトをProcへと変換すべく to_procメソッドが呼び出される。このとき、to_proc メソッドが定義されていれば、それが呼び出される。to_procメソッドは、Procオブジェクトを返す。