Understanding Ruby Closure

I'm trying to better understand Ruby closures, and I came across this example code that I don't quite understand:

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

      

Can someone help me understand what is actually going on in the above code when I call c = make_counter

? In my opinion, here's what I think is happening:

Ruby calls a method make_counter

and returns a Proc object, where the block of code associated with Proc will be { n = 1 }

. When the first is executed c.call

, the Proc object will execute the block associated with it and return n = 1

. However, when the second is executed c.call

, isn't the Proc object its associated block that is still running { n = 1 }

? I don't understand why the output will change to 2.

I may not understand this at all, and it would be helpful if you could provide some clarification on what actually happens in Ruby.

+3


source to share


1 answer


The block is not evaluated when called make_counter

. The block is evaluated and run when you call Proc through c.call

. Therefore, every time you run c.call

, the expression n = n + 1

will be evaluated and executed. Binding for Proc will cause the variable to n

remain in scope because it (a local variable n

) was first declared outside the Proc closure. Thus, it n

will continue to increase with each iteration.

To clarify this further:



  • The block that Proc (or lambda) defines is not evaluated on initialization - the code inside is frozen exactly as you see it.
  • Okay, the code is actually "evaluating", but not for the purpose of modifying the frozen code. Rather, it is checked for any variables that are currently in scope that are used in the context of the Proc code. Since it n

    is a local variable (as it was previously defined) and it is used in Proc, it is fixed in the binding and goes forward to ride.
  • When the method call

    is called in Proc, it will execute the frozen code in the binding context that was captured. So n

    , which was originally assigned as 0, it increases to 1. When called again, the same one n

    will increase again to 2. And so on ...
+8


source







All Articles