3

我正在尝试覆盖 Enumerable 模块上的方法,如下所示:

module Enumerable
  def collect(&block)
    puts 'collect'
    super
  end
end

(注意这是一个简单的例子)。

理论上,当我调用collector时map,Ruby 应该使用我覆盖的版本,对吗?但事实并非如此。它总是使用内置的 Enumerable 方法。是不是因为collect实际上是enum_collect遵从源头的?

[1,2,3].map(&:to_s) # never prints anything

是的,我知道 Monkey-Patching 很糟糕,等等等等,我知道还有其他选择,包括子类化等,但我想知道是否可以用 Ruby 覆盖内置的 C 函数。

Enumerable.class_eval do
  def collect(&block)
    puts 'collect was class_eval'
    super
  end
end

 

eigen = class << Enumerable; self; end
eigen.class_eval do
  def collect(&block)
    puts 'collect was eigen'
    super
  end
end

 

module Enumerable
  def collect(&block)
    puts 'collect was opened up'
    super
  end
end

 

Array.send(:include, Enumerable)

以及几乎所有的组合。

PS。这是 Ruby 1.9.3,但理想情况下,我正在寻找一种适用于所有版本的解决方案。

4

2 回答 2

5

我认为您的问题是 Array 定义了自己的collect方法,而不是使用 Enumerable 的:

收集{|物品| 方块 } → new_ary
地图 {|item| 块 } → new_ary
收集 → an_enumerator
映射 → an_enumerator

为 的每个元素调用一次self。创建一个包含块返回值的新数组。另请参阅Enumerable#collect

因此,您可以随心所欲地修补Enumerable#collect所有内容,但Array不在乎,因为它不使用Enumerable#collect. 如果您使用猴子补丁,您将会有更好的运气Array#collect

class Array
  def collect
    #...
  end
end

您也需要打补丁Array#map或只是打补丁map并让别名处理collect.

请注意,这Array#map是在 C 中实现的,因此 C 部分与您的问题无关。

于 2012-12-30T01:19:58.870 回答
0

您可以使用同一组方法在 Ruby 中迭代许多不同类型的对象。例如,您可以使用包含?方法来迭代数组和哈希以查看它们是否包含特定对象,并且类似地使用 map 方法来转换它们。你可能认为每个方法都自己实现了这些方法,但你错了。

在 Ruby 中,这些类型的方法在 Enumerable 模块中实现。如果你的类实现了 each 方法并包含 Enumerable 模块,那么你的类将响应大量涉及集合的消息,包括迭代、映射、搜索等。

于 2014-07-31T17:14:03.147 回答