简体   繁体   中英

Ruby equivalent of python nonlocal

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?

Maybe something like:

class CGroup
  def counter
    @x ||= 0
    lambda do |y|
      @x += y
    end
  end
end

Then:

group = CGroup.new
c = group.counter
c.call(1)
=> 1
c.call(1)
=> 2

I'm not aware of a direct analog for Python's nonlocal .

EDIT : The instance variable is unnecessary, and the same thing can be achieved with a variable local to the method. This makes the class superfluous, although much in Ruby takes place within the context of an object.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM