Я хочу функцию, которая сохраняет локальное состояние в Ruby. Каждый раз, когда я вызываю функцию, я хочу возвратить результат, который зависит и от аргумента вызова и от сохраненного состояния функции. Вот простой пример:
def inc_mult(factor)
@state ||= 0 # initialize the state the first time.
@state += 1 # adjust the internal state.
factor * @state
end
Обратите внимание, что состояние инициализируется в первый раз, но доступ последующих вызовов сохраненное состояние. Это хорошо, за исключением того, что @state
утечки в окружающий контекст, который я не хочу.
Каков самый изящный способ переписать это так, чтобы @state
не протекает?
(Примечание: Мой фактический пример намного более сложен, и инициализация состояния является дорогой.)
Вероятно, вы захотите инкапсулировать inc_mult
в собственный класс, поскольку вы хотите инкапсулировать его состояние отдельно от содержащего его объекта. Именно так работают генераторы (оператор yield
) в Python и C#.
Что-то простое, как это, сделает это:
class Foo
state = 0
define_method(:[]) do |factor|
state += 1
factor * state
end
end
Философски, я думаю, то, к чему вы стремитесь, несовместимо с представлением Ruby о методах как о сообщениях, а не как о функциях, которые могут быть в некотором роде самостоятельными.
Функции не сохраняют состояние. Это процедурный кодекс. Классы содержат код состояния, а также процедурный код. Самый элегантный способ сделать это - следовать правильной парадигме программирования:
Класс для поддержания состояния
Функция для управления состоянием
Поскольку вы используете Ruby, это может показаться Немного более элегантно для вас, чтобы поместить эти вещи в модуль, который может быть включен. Модуль может поддерживать состояние, и метод можно просто вызвать через:
require 'incmodule'
IncModule::inc_mult(10)
или что-то подобное
Отсоединенный сеанс, который не отвечает в сеансе экрана, можно отключить, выполнив следующие действия.
Введите screen -list
для идентификации сеанса отсоединенного экрана.
~ $ screen -list Имеются экраны на: 20751.Melvin_Peter_V42 (Отдельно)
Примечание: 20751.Melvin _ Peter _ V42
- идентификатор сеанса.
Присоедините к отсоединенному экранному сеансу экран
-r 20751.Melvin_Peter_V42
Once, подключенный к сеансу, нажмите Ctrl + A , затем введите quit
Посмотрите на то, что Карл Петерсон разработал для этого в Vista:
-121--3064123-Кажется, что можно просто использовать глобальную переменную или переменную класса в каком-либо другом классе, что, по крайней мере, позволит пропустить непосредственно окружающий контекст.
Ну, вы можете немного поиграть... Как насчет функции, которая переписывает сама себя?
def imult(factor)
state = 1;
rewrite_with_state(state+1)
factor*state
end
def rewrite_with_state(state)
eval "def imult(factor); state = #{state}; rewrite_with_state(#{state+1}); factor*state; end;"
end
Предупреждение: Это крайне некрасиво и не должно использоваться в производственном коде!
Мне нужна функция, которая сохраняет локальное состояние в Ruby.
Это слово "функция" должно немедленно вызвать большой жирный красный мигающий предупреждающий знак, что вы используете неправильный язык программирования. Если вам нужны функции, вы должны использовать функциональный язык программирования, а не объектно-ориентированный. В функциональном языке программирования функции обычно замыкаются на своем лексическом окружении, что делает то, что вы пытаетесь сделать, абсолютно тривиальным:
var state;
function incMult(factor) {
if (state === undefined) {
state = 0;
}
state += 1;
return factor * state;
}
print(incMult(2)); // => 2
print(incMult(2)); // => 4
print(incMult(2)); // => 6
Этот конкретный пример приведен в ECMAScript, но он выглядит более или менее одинаково в любом функциональном языке программирования.
[Примечание: я знаю, что это не очень хороший пример, потому что ECMAScript на самом деле также является объектно-ориентированным языком, и поскольку в нем нарушена семантика области видимости, это фактически означает, что состояние
утекает и в этом случае. В языке с правильной семантикой области видимости (а через пару лет ECMAScript станет одним из них) это будет работать так, как задумано. Я использовал ECMAScript в основном из-за его знакомого синтаксиса, а не как пример хорошего функционального языка.]
Именно таким образом состояние инкапсулируется в функциональных языках с тех пор, как, ну, с тех пор, как существуют функциональные языки, вплоть до лямбда-исчисления.
Однако в 1960-х годах некоторые умные люди заметили, что это очень распространенный паттерн, и решили, что этот паттерн настолько распространен, что заслуживает собственной языковой особенности. Так появился объект.
Итак, в объектно-ориентированном языке, вместо использования функциональных закрытий для инкапсуляции состояния, вы будете использовать объекты. Как вы могли заметить, методы в Ruby не замыкаются на своем лексическом окружении, в отличие от функций в функциональных языках программирования. И именно по этой причине: потому что инкапсуляция состояния достигается другими средствами.
Итак, в Ruby вы бы использовали объект следующим образом:
inc_mult = Object.new
def inc_mult.call(factor)
@state ||= 0
@state += 1
factor * @state
end
p inc_mult.(2) # => 2
p inc_mult.(2) # => 4
p inc_mult.(2) # => 6
[Примечание: Это соответствие 1:1 - то, о чем говорят функциональные программисты, когда говорят, что "объекты - это просто бедные закрытия". Конечно, объектно-ориентированные программисты обычно возражают: "Закрытия - это просто объекты бедняка". И самое забавное, что оба они правы, но ни один из них этого не осознает.]
Теперь, для полноты картины, я хочу отметить, что хотя методы не замыкаются на своем лексическом окружении, в Ruby есть одна конструкция, которая замыкает: блоки. (Интересно, что блоки не являются объектами.) И, поскольку вы можете определять методы с помощью блоков, вы также можете определять методы, которые являются закрытиями:
foo = Object.new
state = nil
foo.define_singleton_method :inc_mult do |factor|
state ||= 0
state += 1
factor * state
end
p foo.inc_mult(2) # => 2
p foo.inc_mult(2) # => 4
p foo.inc_mult(2) # => 6