3

私はRubyクロージャーをよりよく理解しようとしていますが、よくわからないこのサンプルコードに出くわしました:

def make_counter
  n = 0
  return Proc.new { n = n + 1 }
end

c = make_counter
puts c.call # => this outputs 1
puts c.call # => this outputs 2

を呼び出すと、上記のコードで実際に何が起こるかを誰かが理解するのを手伝ってくれますc = make_counterか? 私の考えでは、次のことが起こっていると思います。

Ruby はmake_counterメソッドを呼び出し、Proc に関連付けられたコード ブロックが存在する Proc オブジェクトを返します{ n = 1 }。最初c.callのオブジェクトが実行されると、Proc オブジェクトはそれに関連付けられたブロックを実行し、 を返しますn = 1。しかし、2 番目c.callが実行されると、Proc オブジェクトは関連付けられたブロックをまだ実行していません{ n = 1 }か? 出力が2に変わる理由がわかりません。

私はこれをまったく理解していない可能性があります。Ruby 内で実際に何が起こっているのかを明確にしていただけると助かります。

4

3 に答える 3

8

make_counterが呼び出されたときに、ブロックは評価されません。を介して Proc を呼び出すと、ブロックが評価されて実行されますc.call。したがって、 を実行するたびc.callに、式n = n + 1が評価されて実行されます。Proc のバインドにより、n変数 (ローカルn変数) が最初に Proc クロージャの外で宣言されたため、変数はスコープ内に残ります。そのため、n反復ごとに増加し続けます。

これをさらに明確にするために:

  • Proc (またはラムダ) を定義するブロックは、初期化時に評価されません。内部のコードは、表示どおりに凍結されます。
  • OK、コードは実際に「評価」されますが、凍結されたコードを変更する目的ではありません。代わりに、現在スコープ内にあり、Proc のコード ブロックのコンテキスト内で使用されている変数がチェックされます。nはローカル変数であり (前の行で定義されているように)、Proc 内で使用されるため、バインディング内でキャプチャされ、乗り物に付随します。
  • メソッドが Proc で呼び出されると、callキャプチャされたバインディングのコンテキスト内で「凍結された」コードが実行されます。そのためn、元々 0 として割り当てられていた は 1 にインクリメントされます。再度呼び出されると、同じものnは再び 2 にインクリメントされます。など...
于 2013-02-15T04:35:17.700 に答える