Ruby: Resumable функционирует с аргументами

Я хочу функцию, которая сохраняет локальное состояние в Ruby. Каждый раз, когда я вызываю функцию, я хочу возвратить результат, который зависит и от аргумента вызова и от сохраненного состояния функции. Вот простой пример:

def inc_mult(factor)
  @state ||= 0   # initialize the state the first time.
  @state += 1    # adjust the internal state.
  factor * @state
end

Обратите внимание, что состояние инициализируется в первый раз, но доступ последующих вызовов сохраненное состояние. Это хорошо, за исключением того, что @state утечки в окружающий контекст, который я не хочу.

Каков самый изящный способ переписать это так, чтобы @state не протекает?

(Примечание: Мой фактический пример намного более сложен, и инициализация состояния является дорогой.)

5
задан Peter 22 February 2010 в 02:53
поделиться

5 ответов

Вероятно, вы захотите инкапсулировать inc_mult в собственный класс, поскольку вы хотите инкапсулировать его состояние отдельно от содержащего его объекта. Именно так работают генераторы (оператор yield) в Python и C#.

Что-то простое, как это, сделает это:

class Foo 
  state = 0 
  define_method(:[]) do |factor|
    state += 1
    factor * state
  end 
end

Философски, я думаю, то, к чему вы стремитесь, несовместимо с представлением Ruby о методах как о сообщениях, а не как о функциях, которые могут быть в некотором роде самостоятельными.

4
ответ дан 14 December 2019 в 08:49
поделиться

Функции не сохраняют состояние. Это процедурный кодекс. Классы содержат код состояния, а также процедурный код. Самый элегантный способ сделать это - следовать правильной парадигме программирования:

Класс для поддержания состояния
Функция для управления состоянием

Поскольку вы используете Ruby, это может показаться Немного более элегантно для вас, чтобы поместить эти вещи в модуль, который может быть включен. Модуль может поддерживать состояние, и метод можно просто вызвать через:

require 'incmodule'
IncModule::inc_mult(10)

или что-то подобное

2
ответ дан 14 December 2019 в 08:49
поделиться

Отсоединенный сеанс, который не отвечает в сеансе экрана, можно отключить, выполнив следующие действия.

  1. Введите screen -list для идентификации сеанса отсоединенного экрана.

     ~ $ screen -list
    Имеются экраны на:
    20751.Melvin_Peter_V42 (Отдельно)
    

    Примечание: 20751.Melvin _ Peter _ V42 - идентификатор сеанса.

  2. Присоедините к отсоединенному экранному сеансу экран

     -r 20751.Melvin_Peter_V42
  3. Once, подключенный к сеансу, нажмите Ctrl + A , затем введите quit

-121--1766262-

Посмотрите на то, что Карл Петерсон разработал для этого в Vista:

SendInput

-121--3064123-

Кажется, что можно просто использовать глобальную переменную или переменную класса в каком-либо другом классе, что, по крайней мере, позволит пропустить непосредственно окружающий контекст.

0
ответ дан 14 December 2019 в 08:49
поделиться

Ну, вы можете немного поиграть... Как насчет функции, которая переписывает сама себя?

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

Предупреждение: Это крайне некрасиво и не должно использоваться в производственном коде!

0
ответ дан 14 December 2019 в 08:49
поделиться

Мне нужна функция, которая сохраняет локальное состояние в 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
1
ответ дан 14 December 2019 в 08:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: