Как я могу memoize метод, который может возвращать true, false или nil в Ruby?
Очевидно, что ||=
не будет работать
def x?
@x_query ||= expensive_way_to_calculate_x
end
потому что если он окажется false
или nil
, то expensive_way_to_calculate_x
будет запущен снова и снова.
В настоящее время лучшим способом, который я знаю, является значение Array
:
def x?
return @x_query.first if @x_query.is_a?(Array)
@x_query = [expensive_way_to_calculate_x]
@x_query.first
end
Есть ли более обычный или эффективный способ сделать это?
ОБНОВЛЕНИЕ. Я понял, что мне хотелось memoize nil
в дополнение к false
- это все возвращается к https://rails.lighthouseapp.com/projects/8994/tickets/1830-railscachefetch-does-not-work-with-false-boolean-as-cached-value - мои извинения Эндрю Маршалл, которые дали совершенно правильный ответ.
Ответы
Ответ 1
Явным образом проверьте, есть ли значение @x_query
nil
:
def x?
@x_query = expensive_way_to_calculate_x if @x_query.nil?
@x_query
end
Обратите внимание, что если это не была переменная экземпляра, вам нужно было бы проверить, было ли это определено также/вместо этого, поскольку все переменные экземпляра по умолчанию равны nil
.
Учитывая ваше обновление, что @x_query
memoized value может быть nil
, вы можете использовать defined?
вместо этого, чтобы обойти тот факт, что все переменные экземпляра по умолчанию равны nil
:
def x?
defined?(@x_query) or @x_query = expensive_way_to_calculate_x
@x_query
end
Обратите внимание, что выполнение чего-то вроде a = 42 unless defined?(a)
не будет работать так, как ожидалось, так как после синтаксического анализатора a =
, a
определяется до того, как он достигнет условного. Однако это не относится к переменным экземпляра, так как они по умолчанию равны nil
анализатор не определяет их при достижении =
. Несмотря на это, я считаю хорошей идеей использовать длинную блочную форму or
или unless
вместо однострочной unless
с defined?
, чтобы она была последовательной.
Ответ 2
Для учета nil
используйте defined?
, чтобы определить, была ли определена переменная:
def x?
return @x_query if defined? @x_query
@x_query = expensive_way_to_calculate_x
end
defined?
вернет nil
, если переменная не определена или строка "instance_variable"
иначе:
irb(main):001:0> defined? @x
=> nil
irb(main):002:0> @x = 3
=> 3
irb(main):003:0> defined? @x
=> "instance-variable"