3.メタプログラミングパターン-20キュウ。 短絡

前の投稿で 、最も重要な概念であるクロージャについて触れました。

この概念の本質は、ブロックが作成されたコンテキストで見えるように、どのブロックでも、 「周囲の世界全体」であるということです。 全世界(名前空間)がブロックに含まれているのではなく、世界(名前空間)の視点が固定されていると言う方が正しいです。





次の例を確認した後、この段落を再度読み直してください。



例を理解するには、ブロック、メソッドの概念を独自に理解しておくと便利です。 Proc#call



Proc#call



lambda



構成体、およびクラスインスタンス変数(インスタンス変数は名前が犬で始まる変数)およびクラス変数(クラス変数は名前が2つの犬で始まる変数)の概念とともに:



したがって、コード例:



例1

a = 1 <br>

b = lambda { puts a }<br>

b.call # 1 <br>

a = 2 <br>

b.call # 2 <br>

# - <br>







例2

class 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



すべてのインスタンス変数が表示されるという事実によって説明されます。



したがって、オブジェクトの内部コンテキストは、オブジェクト内で作成されたブロックを使用して引き出し、何らかの機能の結果として外部に転送できます。



例3

class 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番目の引数として渡すことができます:「このコンテキストでこのコードを実行する」:



例4

class 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







例5

class 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









計算時間



閉鎖などの喜びのために、あなたは支払う必要があります。



参照資料






All Articles