前の投稿で 、最も重要な概念であるクロージャについて触れました。
この概念の本質は、ブロックが作成されたコンテキストで見えるように、どのブロック
でも、 「周囲の世界全体」であるということです。 全世界(名前空間)がブロックに含まれているのではなく、世界(名前空間)の視点が固定されていると言う方が正しいです。
次の例を確認した後、この段落を再度読み直してください。
例を理解するには、ブロック、メソッドの概念を独自に理解しておくと便利です。
Proc#call Proc#call 、
lambda構成体、およびクラスインスタンス変数(インスタンス変数は名前が犬で始まる変数)およびクラス変数(クラス変数は名前が2つの犬で始まる変数)の概念とともに:
Proc Proc式で直接作成できる名前のない(匿名の)メソッドと呼ばれるブロックのクラス。- 式
b.call(*args)はブロックbを実行し、実行結果を返します。 呼び出しの代わりに、角括弧を使用できます。 lambda {|a,...| ... } lambda {|a,...| ... } -ブロックを作成します。たとえば、 b = lambda {|x,y,z| x+y+z} b = lambda {|x,y,z| x+y+z}は、3つの数値を加算するブロックを作成します。特に、式b[1,2,3]は6を返します。- ブロックはラムダを使用して作成されるだけでなく、メソッドが呼び出されたときに自動的に構築され、その後に構築
{ ... }またはdo ... end続きます。 たとえば、 ary.inject{|a,b| a * b} ary.inject{|a,b| a * b}は、2つの数値の乗算を実行するブロックをinjectメソッドに渡します。 - インスタンス変数はオブジェクト内に存在し、デフォルトでnilに初期化されていると見なされます。
- クラス変数はクラス内に存在し、デフォルトでは初期化されていないと見なされます。 予備的な初期化なしで式で使用すると、
Exception 「 uninitialized class variable .. in ... 」が発生します。
したがって、コード例:
例1a = 1 <br>
b = lambda { puts a }<br>
b.call # 1 <br>
a = 2 <br>
b.call # 2 <br>
# - <br>
例2class Abc <br>
attr_accessor :bar <br>
def foo <br>
@bar ||= 0 <br>
x = 5 <br>
lambda { puts @bar , x, self .class; }<br>
end <br>
end <br>
<br>
x = 10 <br>
a = Abc .new<br>
b = a.foo<br>
b.call # 0, 5 Abc <br>
a.bar += 1 <br>
x = 10 <br>
b.call # 1, 5, Abc <br>
# bar a b, <br>
# ( ) -- <br>
# ; - <br>
# foo, @a x, <br>
# , <br>
# , foo . <br>
<br>
lambdaを使用して作成されたブロックと、メソッドに渡されたブロックの両方で、中括弧を使用して装飾され、
do ... end構文を使用して、ブロックが閉じられます。
最後の例では、クラス
Abcインスタンスaで
fooメソッドを呼び出しました。
このメソッド内では、
@barインスタンス変数が初期化され、ブロックが返されます。
この変数と、ローカル変数
xおよび
self.class値を
self.classます。
このコードを実行すると、ブロックが祖国にどれだけアタッチされているか、すべての考えと動機がそこにあることがわかります。
文字列「
b.call 」が
b.callコンテキストでは、
@bar変数
@bar表示
b.callません(つまり、このコンテキストには存在しません)。
それでも、ブロック
b実行すると
@barオブジェクト
a変数
@bar値が出力されますが、それはここでは不適切です。 これは、ブロックがオブジェクト
a fooメソッドの実行のコンテキストで作成され、このコンテキストではオブジェクト
aすべてのインスタンス変数が表示されるという事実によって説明されます。
したがって、オブジェクトの内部コンテキストは、オブジェクト内で作成されたブロックを使用して引き出し、何らかの機能の結果として外部に転送できます。
例3class Abc <br>
attr_accessor :block <br>
def do_it <br>
@a = 1 <br>
block.call<br>
end <br>
end <br>
<br>
c = 1 <br>
a = Abc .new<br>
a.block = lambda { puts " c= #{ c } " }<br>
a.do_it # 1; <br>
# - <br>
# <br>
<br>
a.block = lambda { puts " @a= #{ @a .inspect } " }<br>
a.do_it # nil, .. @ , <br>
# " " a.block. <br>
# a.block Abc#foo <br>
# Abc#foo a.block <br>
同じことを繰り返しますが、ブロックはメソッドに関連付けられたブロックとして作成され、
lambda構造は使用されません。
class Abc <br>
def do_it (&block)<br>
@a = 1 <br>
block.call<br>
end <br>
end <br>
<br>
c = 1 <br>
a = Abc .new<br>
a.do_it {puts " c= #{ c } " } <br>
a.do_it { puts " @a= #{ @a .inspect } " }<br>
<br>
コンテキストとは何ですか?
これは名前空間の特定の視点であり、そこから何かが見え、何かが見えず、何かが独自の方法で見られます。
たとえば、このメソッドが表示され、
selfがこのオブジェクトに等しいオブジェクトのインスタンス変数は、メソッドの本体から表示されます。 他のオブジェクトのインスタンス変数は表示されません。
特殊な式
selfは、各コンテキストで独自の方法で定義できるメソッドとして考えると便利です。
コンテキストを変更する理由は、
defおよび
classコンストラクトです。 それらは通常、インスタンス変数、クラス変数の可視性の変化、および
self表現の値の変化をもたらします。
通常のブロックも、それが作成されたコンテキストを含むものの、新しいコンテキストです。 ブロックは、独自のローカル変数(およびC内)と引数(特別なローカル変数として解釈される必要があります)を持つことができます。
実際、コンテキストの概念には
Ruby独自の固有のマッピングがあります-それはクラスのオブジェクトです
Binding Binding 各ブロックには
bindingがあり、この
bindingは
evalメソッドの2番目の引数として渡すことができます:「このコンテキストでこのコードを実行する」:
例4class Abc <br>
attr_accessor :x <br>
def inner_block <br>
lambda {| x | x * @factor }<br>
end <br>
end <br>
<br>
a = Abc .new<br>
b = a.inner_block<br>
eval ( " @factor = 7 " , b.binding)<br>
puts b[ 10 ] # 70 <br>
eval ( " @x = 6 * @factor " , b.binding)<br>
puts ax # 42
しかし、もちろん、そのように書く必要はありません。 オブジェクトのコンテキストでコードを実行するには、単に
instance_eval使用し
instance_eval 。
例5class Abc <br>
attr_accessor :x <br>
end <br>
<br>
a = Abc .new<br>
a.instance_eval( " @factor = 7 " )<br>
a.instance_eval( " @x = 6 * @factor " )<br>
puts ax # 42
計算時間
閉鎖などの喜びのために、あなたは支払う必要があります。
参照資料