I am trying to write a closure in Ruby. This is the code written in Python:
def counter():
x = 0
def increment(y):
nonlocal x
x += y
print(x)
return increment
Is there a "nonlocal" equivalent in Ruby so I can access and make changes to the variable x from inside increment?
nonlocal completely encapsulates that information in a namespace dedicated to the particular run of your outer function. There is really no downside here. Using instance attributes makes that information available to anyone using the instance.
Definition and Usage The nonlocal keyword is used to work with variables inside nested functions, where the variable should not belong to the inner function. Use the keyword nonlocal to declare that the variable is not local.
The nonlocal statement. The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first.
The nonlocal
keyword tells Python which variables to capture. In Ruby, you don't need such a keyword: all variables are captured unless explicitly mentioned otherwise.
So, the Ruby equivalent to your Python code translates almost directly:
counter = -> {
x = 0
->y {
x += y
puts x
}
}
i = counter.()
i.(2)
# 2
i.(3)
# 5
It would probably be more idiomatic to use a method for counter
, though:
def counter
x = 0
->y {
x += y
puts x
}
end
i = counter
i.(2)
# 2
i.(3)
# 5
Since there is objection to using an object, why not just use a lambda?
counter_generator = ->(){
x ||= 0
->(y){
x += y
puts x
}
}
i = counter_generator.call
=> #<Proc:0x00000100867508@(irb):17 (lambda)>
i.call(1)
1
=> nil
i.call(1)
2
=> nil
Note that the incrementor actually returns nil because you've only specified to output the value of x, not to return it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With