【问题标题】:Is there a convention for memoization in a method call?在方法调用中是否有用于记忆的约定?
【发布时间】:2012-09-30 20:53:57
【问题描述】:

我想避免在方法调用中重新评估值。到目前为止,我一直在这样做:

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

但它最终很丑陋。在某些代码中,我看到如下内容:

def some_method
  @some_method ||= some_method!
end

private

def some_method!
  # lot's of code
end

我不喜欢结尾的 bang (!),所以我想出了这个:

def some_method
  @some_method ||= _some_method
end

private

def _some_method
  # lot's of code
end
  • 前置下划线是一个好的约定吗?
  • 对于 memoized/non-memoized 方法对是否有其他约定?
  • 是否有一些约定来记忆多行方法?

【问题讨论】:

  • 相关:stackoverflow.com/questions/696338 使用 Rails 的类 ActiveSupport::Memoize 似乎不错:railway.at/articles/2008/09/20/a-guide-to-memoization
  • 您期待什么样的答案? Vise 和 Agis 都提出了好的约定。此外,您的第二个代码块是一个通用约定。
  • 我喜欢||= begin...end。我觉得它很优雅。
  • 由你决定.. __some_method, compute_some_method, calculate_some_method, some_method_impl, __some_method_code
  • 注意:is a convention 关于何时以及为何使用感叹号。我认为这有点超出了公约的目的,但值得考虑。再次稍微偏离这里的相关内容,但coding by convention 具有价值,所以虽然说“我没有修改对象”很好,但说“我不喜欢爆炸”是一个不太好的理由来避免它。

标签: ruby coding-style memoization


【解决方案1】:

我认为还有另一种方式,更像 Java 风格。

首先你应该实现注解,比如“Java-style annotations in Ruby”和“How to simulate Java-like annotations in Ruby?”。

然后你应该添加像 _cacheable 这样的注释,它会告诉方法它应该返回实例变量,如果它是 null 它应该通过调用方法来计算它,这样你的代码会更清晰:

_cacheable
def some_method
   # do_some_work
end

【讨论】:

    【解决方案2】:

    我会这样做:

    def filesize
      @filesize ||= calculate_filesize
    end
    
    private
    
    def calculate_filesize
      # ...
    end
    

    所以我只是给方法命名不同,因为我认为它更有意义。

    【讨论】:

    • 我不喜欢在方法名前放这么多字符来将其标记为未记忆的版本。
    • 为什么?它在语义上是有意义的,并且非常明显它在做什么。对更少角色的盲目追求让您忽略了最佳答案。
    • +1。这些名称准确地描述了函数正在做什么,包括它们的预期性能概况。
    • 即使赏金去了别处,我还是接受了这个,因为它更符合其他 ruby​​ 约定。
    【解决方案3】:

    我使用memoist gem,它可以让您轻松记住一个方法,而无需更改原始方法或创建两个方法。

    因此,例如,不必使用两种方法,file_sizecalculate_file_size,而必须自己使用实例变量来实现记忆:

    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
    

    每个记忆函数都带有一种刷新现有值的方法。

    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】:

      我通常会像 Agis 回答或:

      def filesize() @filesize ||=
        calculate_filesize
      end
      

      顺便说一句:

      我经常使用这种记忆技术:

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

      这将允许您稍后使用一个简单的@_memo.clear 清除所有记忆变量。 @_memo 变量应该像 Hash.new { |h, k| h[k] = Hash.new } 这样初始化。 它为您提供了许多使用 ActiveSupport::Memoize 和might be much slower 的类似元编程技术的冒险。

      【讨论】:

        【解决方案5】:

        我也不喜欢爆炸声。我用

        def some_method 
          @some_method_memo ||= some_method_eval 
        end 
        
        private 
        
        def some_method_eval
          # lot's of code 
        end 
        

        这里evalevaluation 的简写。我喜欢它的阅读方式,也喜欢它使公共界面简洁。

        我鄙视依赖下划线作为区分标记的约定:它们都容易出错并且要求我记住 YAMC(又一个无意义的约定)。 Ada 语言专为安全关键型应用程序而设计,不允许前导、尾随或多个下划线。好主意。

        【讨论】:

        • 最后支持(积极的)长篇大论。烧掉所有 YAMC :)
        【解决方案6】:

        根据您的第一个示例,我通常使用begin, end,但如果有更多代码,我只需查看变量是否存在,无需为此创建另一个方法。

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

        【讨论】:

        • 那行不通。你需要把return放在def的第一行前面。
        • 我唯一不喜欢这种方法的是编码人员需要编辑方法以便在其中的某处分配@some_method 变量,因此在方法的逻辑中混合了记忆逻辑.
        • 我认为您过于苛刻,因为这首先取决于该方法的作用。例如,我有时在 rails helpers 中使用它,它非常有意义。更不用说模块中的 private 关键字一开始就不能很好地工作。附带说明一下,因为您也对 Agis 投了反对票,所以仅仅因为您不完全同意他们而对您收到的所有答案都投反对票是不好的。
        猜你喜欢
        • 1970-01-01
        • 2019-11-21
        • 2013-04-21
        • 1970-01-01
        • 1970-01-01
        • 2017-06-12
        • 2018-04-30
        • 2021-06-24
        相关资源
        最近更新 更多