Есть ли соглашение для memoization в вызове метода?

Я хочу избежать переоценки значения в вызове метода. До сих пор я делал это:

def some_method
  @some_method ||= begin
    # lot of code
  end
end

Но это заканчивается довольно уродливо. В некотором коде я увидел что-то вроде следующего:

def some_method
  @some_method ||= some_method!
end

private

def some_method!
  # lot of code
end

Мне не нравится бит (!) в конце, поэтому я придумал следующее:

def some_method
  @some_method ||= _some_method
end

private

def _some_method
  # lot of code
end
  • Является ли добавление с подчеркиванием хорошим соглашением?
  • Есть ли другое соглашение для memoized/non-memoized пар методов?
  • Есть ли какое-то соглашение для memoize многострочных методов?

Ответы

Ответ 1

Я бы сделал это следующим образом:

def filesize
  @filesize ||= calculate_filesize
end

private

def calculate_filesize
  # ...
end

Поэтому я бы назвал этот метод по-другому, так как я думаю, что это имеет смысл.

Ответ 2

Есть еще один способ, более явный стиль Java.

Прежде всего, вы должны реализовать аннотации, такие как аннотации в стиле Java в Ruby "и" Как имитировать Java-подобные аннотации в Ruby?".

Затем вы должны добавить аннотацию как _cacheable, которая будет указывать методу, что она должна возвращать переменную экземпляра, и если она равна null, она должна вычислять ее путем вызова метода, поэтому ваш код будет более понятным:

_cacheable
def some_method
   # do_some_work
end

Ответ 3

Я использую memoist gem, который позволяет легко запоминать метод без необходимости изменения исходного метода или создания двух методов.

Так, например, вместо двух методов file_size и calculate_file_size, и вам нужно реализовать memoization самостоятельно с переменной экземпляра:

def file_size
  @file_size ||= calculate_file_size
end

def calculate_file_size
  # code to calculate the file size
end

вы можете просто сделать это:

def file_size
  # code to calculate the file size
end
memoize :file_size

Каждая memoized функция имеет способ сбросить существующее значение.

object.file_size       # returns the memoized value
object.file_size(true) # bypasses the memoized value and rememoizes it

Таким образом, вызов object.file_size(true) будет эквивалентным вызову object.calculate_file_size...

Ответ 4

Обычно я использую begin, end в соответствии с вашим первым примером, но если есть еще немного кода, я просто смотрю, существует ли переменная, нет необходимости создавать другой метод для этого.

def some_method
  return @some_method if @some_method
  # lot of code
  @some_method
end

Ответ 5

Мне тоже не нравится. Я использую

def some_method 
  @some_method_memo ||= some_method_eval 
end 

private 

def some_method_eval
  # lot of code 
end 

Здесь eval является сокращением для evaluation. Мне нравится, как это читается, а также упрощает публичный интерфейс.

Я презираю соглашения, которые полагаются на подчеркивания как отличительные знаки: они оба склонны к ошибкам и требуют, чтобы я помнил YAMC (еще одно бессмысленное соглашение). Язык Ada, предназначенный для критически важных приложений, не позволяет вести ведущие, конечные или множественные символы подчеркивания. Хорошая идея.

Ответ 6

Я обычно делаю это как в Agis или:

def filesize() @filesize ||=
  calculate_filesize
end

BTW:

Я часто использую этот метод memoization:

def filesize() @_memo[:filesize] ||=
  calculate_filesize
end

Это позволит вам позже очистить все замеченные переменные одним простым @_memo.clear. Значение переменной @_memo должно быть инициализировано следующим образом: Hash.new { |h, k| h[k] = Hash.new }. Это дает вам много преимуществ использования ActiveSupport:: Memoize и аналогичных метапрограммированных методов, которые могут быть намного медленнее.