从Array继承时获取#with_index

时间:2017-01-14 18:50:35

标签: ruby oop inheritance

我希望我的SomeArray#map返回SomeArray类的“数组”。

class SomeArray < Array
  def map
    SomeArray.new(super)
  end
end

some_array = SomeArray.new(["foo", "bar", "baz"])
p some_array.class #=> SomeArray
p some_array.map { |e| e }.class #=> SomeArray

除此之外我还希望能够使用Enumerator#with_index实例方法。理想情况下这样的事情会起作用:

some_array.map.with_index { |e, i| e }.class #=> SomeArray

那怎么办?

我试过了:

class SomeArray < Array
  def map
    SomeArray.new(super)
  end

  def with_index(offset = 0)
    super
  end
end

some_array = SomeArray.new(["foo", "bar", "baz"])
p some_array.class #=> SomeArray
p some_array.map.with_index { |e, i| e }.class #=> no implicit conversion of Enumerator into Integer (TypeError)

但它不起作用。

2 个答案:

答案 0 :(得分:1)

我认为这里的问题是你将可枚举和数组视为相同,而不是它们。

具体来说,这是在地图调用中:SomeArray.new(super)

我可以重现你的错误:

[6] pry(main)> Array.new [1].map
TypeError: no implicit conversion of Enumerator into Integer

现在,当您将一个块传递给map时,它可以正常工作:

Array.new([1].map { |x| x })  
=> [1]

但在你的map.with_index中,你没有这样做。

您可以这样做:

module Foo
  include Enumerable
  def map
    puts "calling my map"
    super
  end
  def with_index
    puts "calling my with_index"
    super
  end
end

class MyArr < Array
  include Foo
end

puts MyArr.new([1]).map.with_index { |x, y| [x,y] }
# calling my map
# calling my with_index
# 1
# 0

问题是为什么你要编写这个只调用super的类的问题。但是如果要修改枚举的默认功能,这是一种方法。

答案 1 :(得分:1)

主要问题是map是一种数组方法,而with_index是一种Enumerator方法。

  • 数组方法定义为只调用super,并将输出数组转换为SomeArray
  • 首先使用to_enum定义枚举器方法,将输出转换为数组,然后转换为SomeArray

它可能不是你想要做的最好的结构,也不是很有效。这只是一个概念证明!

class SomeArray < Array
  # Array methods are overwritten here :
  [:map, :select, :reject].each do |array_method_name|
    define_method array_method_name do |*p, &block|
      SomeArray.new(super(*p, &block).to_a)
    end
  end

  # Enumerator methods are defined for SomeArray here :
  [:with_index, :with_object].each do |enum_method_name|
    define_method enum_method_name do |*p, &block|
      SomeArray.new(to_enum.public_send(enum_method_name, *p, &block).to_a)
    end
  end
end

some_array = SomeArray.new(%w(foo bar baz biz))

p some_array.map { |s| s * 2 }.with_index.select { |_, i| i.even? }
#=> [["foofoo", 0], ["bazbaz", 2]]
p some_array.map { |s| s * 2 }.with_index.select { |_, i| i.even? }.class
#=> SomeArray