Рубиновый эквивалент нелокального питона
Я пытаюсь написать закрытие в Ruby. Это код, написанный на Python:
def counter():
x = 0
def increment(y):
nonlocal x
x += y
print(x)
return increment
Есть ли "нелокальный" эквивалент в Ruby, чтобы я мог получить доступ и внести изменения в переменную x изнутри приращения?
3 ответа
nonlocal
Ключевое слово сообщает Python, какие переменные для захвата. В Ruby вам не нужно такое ключевое слово: все переменные фиксируются, если явно не указано иное.
Итак, эквивалентный Ruby вашему коду Python переводится почти напрямую:
counter = -> {
x = 0
->y {
x += y
puts x
}
}
i = counter.()
i.(2)
# 2
i.(3)
# 5
Вероятно, было бы более идиоматичным использовать метод для counter
, хоть:
def counter
x = 0
->y {
x += y
puts x
}
end
i = counter
i.(2)
# 2
i.(3)
# 5
Поскольку существует возражение против использования объекта, почему бы просто не использовать лямбду?
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
Обратите внимание, что инкрементор фактически возвращает nil, потому что вы указали только вывод значения x, а не его возвращение.
Может быть что-то вроде:
class CGroup
def counter
@x ||= 0
lambda do |y|
@x += y
end
end
end
Затем:
group = CGroup.new
c = group.counter
c.call(1)
=> 1
c.call(1)
=> 2
Я не знаю о прямом аналоге для Python nonlocal
,
РЕДАКТИРОВАТЬ: переменная экземпляра не требуется, и то же самое может быть достигнуто с помощью переменной, локальной для метода. Это делает класс излишним, хотя многое в Ruby происходит в контексте объекта.