前の投稿で 、最も重要な概念であるクロージャについて触れました。
この概念の本質は、ブロックが作成されたコンテキストで見えるように、どのブロック
でも、 「周囲の世界全体」であるということです。 全世界(名前空間)がブロックに含まれているのではなく、世界(名前空間)の視点が固定されていると言う方が正しいです。
次の例を確認した後、この段落を再度読み直してください。
例を理解するには、ブロック、メソッドの概念を独自に理解しておくと便利です。
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
計算時間
閉鎖などの喜びのために、あなたは支払う必要があります。
参照資料