Ruby 哪一个更有效:回应?还是一种什么都不做的方法?

Ruby 哪一个更有效:回应?还是一种什么都不做的方法?,ruby,Ruby,这是我多年来一直想知道的事情:检查一个对象是否有方法,然后,如果有,调用该方法,或者总是有一个什么都不做的方法,总是调用该方法,子类应该根据需要重写该方法,这样会更有效吗 考虑这个例子: class Base def mymethod respond_to?(:hook) and hook() puts 'stuff in mymethod' end end class SubClass < Base def hook

这是我多年来一直想知道的事情:检查一个对象是否有方法,然后,如果有,调用该方法,或者总是有一个什么都不做的方法,总是调用该方法,子类应该根据需要重写该方法,这样会更有效吗

考虑这个例子:

class Base
    def mymethod
        respond_to?(:hook) and hook()
        puts 'stuff in mymethod'
    end
end

class SubClass < Base
    def hook
        puts 'stuff in hook'
    end
end
现在,您不必每次都检查是否有一个名为
hook
的方法,但您必须每次都调用该方法,即使它不起任何作用


那么哪一个更有效呢?哪一个对程序员来说更容易理解?差异是否如此之小,以至于仅仅是个人喜好的问题?

简短的回答:这太微不足道了,你不应该在意

长答覆:


require 'benchmark'

class Base
  def mymethod
    respond_to?(:hook) and hook()
  end
end

class SubClass < Base
  def hook
  end
end

class Parent
  def mymethod
    hook
  end

  def hook
  end
end

class Child < Parent
end

s = SubClass.new
c = Child.new
n = 10000000
Benchmark.bm(7) do |x|
  x.report(:respond_to?) { n.times {s.mymethod} }
  x.report(:empty) { n.times {c.mymethod} }
end

因此,使用
respond\u to?
比使用empty方法稍微慢一点。

有点离题,但我要说的是,使用
respond\u to?
和一个隐式接收者,就像你建议的那样,是一种代码气味——这意味着你正在做一些奇怪的事情,打破了子类型()

有时,可能真的需要一些黑色元编程魔术、调试、热修复live app等,但我敢打赌在这种情况下,你不会问这样的问题(你肯定知道为什么要这样做,最终效率肯定不是什么问题)


所以,对我来说,问题不在于效率。如果可以,请始终“尊重”(遵循)LSP,只有在必须时才使用
响应?
(在这种情况下,性能可能不是主要标准)

响应?
将始终较慢。它将引起至少一个(在否定的情况下)方法调用,可能还有两个(在肯定的情况下)方法调用。另一方面,空的基本方法案例只是一个方法调用


方法调用速度很快,但如果您确实在尝试微优化,则所有其他方法都保持不变,减少方法调用的数量将导致更快的代码。

哪种方法更有效?使用
Benchmark
并计算出它。我看不到你自己会做出任何努力来回答这个问题,而且这非常容易理解。请至少自己先做些努力,并解释一下你尝试了什么以及你现在陷入了什么困境。什么更容易?还是个人喜好的问题?这完全是基于意见的,所以我的建议是删除这些问题。多年来,我一直在努力寻找不同之处,但没有得出结论。我不是一个有经验的基准测试者,这就是为什么我要去一个可以问基准测试问题的地方。对于一个偏离主题的观点来说,像你这样消极的反应可能会使世界变得更糟糕,这或许也是一个话题,也许你应该考虑一下你的帖子的基调。如果你在过去的几年里做出了各种努力,并且你之前已经对自己进行了测试,那么你必须把这些例子包含在你的文章中。为什么你故意不包括你已经做过的工作,而为你请求帮助的人创造更多的工作?你想让我们复制你的作品?这是我能想象到的从别人那里寻求帮助的最不礼貌的事情。这篇文章高呼“我什么都没试过,我已经没有选择了。”你真的需要花点时间来阅读。我很乐意帮助别人。(如果他们先自助)“程序员浪费大量时间考虑或担心其程序中非关键部分的速度,而在考虑调试和维护时,这些提高效率的尝试实际上会产生强烈的负面影响。我们应该忘记小效率,比如说97%的时间:过早优化是万恶之源。然而,我们不应该错过关键的3%的机会。”——Donald Knuth在《带go-to语句的结构化编程》中说:“我很抱歉你这么想。”?真的吗?@Anotherm为你提供了一些与SO合作的好建议。”、“和”“你问的时候读得很好。啊,我明白你的意思。您基本上是说,在这两种情况下,您总是进行至少一个函数调用,但在一种情况下,您有时会进行两个函数调用。所以总是打一个电话是有道理的。FWIW,我也认为有一个什么都不做的方法更容易理解,尽管我想这是一个(我敢说)意见的问题。非常有趣的东西。我承认那篇文章的大部分内容我都看不懂,但我确实得到了一些外卖。如果我理解正确,LSP说,在我的例子中,Base的任何子类都应该像Base本身一样运行。所以,无论汽车做什么,旅行车都应该做。若汽车并没有规定每加仑的英里数,那个么一辆旅行车可以以每加仑英里数行驶,而不再是一辆汽车。但是,如果一辆车必须符合规定的停车位大小,那么18轮车就不能被视为一辆车。我听到你说的是,当你可以编程时,不要进行元编程。这似乎是一个合理的哲学,非常有用。非常感谢。我承认我没有想过对这个具体案例进行基准测试。这并不是说我没有“任何努力”(如上所述)。。。实际上,我花了一些时间仔细地整理了一个例子,写了一个问题,校对了一下,等等。我只是在发布问题之前不知道答案,我想这就是你发布问题的原因。谢谢实际上,你一点努力也没有。你说“给定这个例子,哪个更快?”但没有努力确定哪个更快。你可以试着对其进行基准测试,然后发布一个关于“我如何对其进行基准测试?我正在尝试的东西不起作用”的问题。相反,你说“有人为我进行基准测试并发布结果。我懒得尝试。”这是零努力。有明显的区别,很抱歉你这么想。

require 'benchmark'

class Base
  def mymethod
    respond_to?(:hook) and hook()
  end
end

class SubClass < Base
  def hook
  end
end

class Parent
  def mymethod
    hook
  end

  def hook
  end
end

class Child < Parent
end

s = SubClass.new
c = Child.new
n = 10000000
Benchmark.bm(7) do |x|
  x.report(:respond_to?) { n.times {s.mymethod} }
  x.report(:empty) { n.times {c.mymethod} }
end
                   user     system      total        real
respond_to?    1.263308   0.011612   1.274920 (  1.327659)
empty          0.789442   0.010208   0.799650 (  0.900536)