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

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


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

例を理解するには、ブロック、メソッドの概念を独自に理解しておくと便利です。 Proc#call Proc#calllambda構成体、およびクラスインスタンス変数(インスタンス変数は名前が犬で始まる変数)およびクラス変数(クラス変数は名前が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があり、このbindingevalメソッドの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


計算時間


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

参照資料


Source: https://habr.com/ru/post/J50665/


All Articles