我有一个对象结果包含一个result对象数组以及一些有关数组中对象的缓存统计信息.我希望Results对象能够像数组一样运行.我的第一个切入是添加这样的方法
def <<(val)
@result_array << val
end
Run Code Online (Sandbox Code Playgroud)
这感觉非常像c,我知道Ruby有更好的方法.
我也希望能够做到这一点
Results.each do |result|
result.do_stuff
end
Run Code Online (Sandbox Code Playgroud)
但我不确定这种each方法在引擎盖下真正做了什么.
目前我只是通过一个方法返回底层数组并调用它们,这似乎不是最优雅的解决方案.
任何帮助,将不胜感激.
Chu*_*uck 63
对于实现类数组方法的一般情况,是的,您必须自己实现它们.Vava的答案显示了这方面的一个例子.但是,在您给出的情况下,您真正想要做的是将处理任务each(以及可能的其他方法)委托给包含的数组,并且可以自动化.
require 'forwardable'
class Results
include Enumerable
extend Forwardable
def_delegators :@result_array, :each, :<<
end
Run Code Online (Sandbox Code Playgroud)
这个类将获得Array的所有Enumerable行为以及Array <<运算符,它将全部通过内部数组.
请注意,当您将代码从Array继承切换到此技巧时,您的<<方法将开始返回不是对象本身,就像真正的数组<<一样 - 这可能会花费您每次使用时声明另一个变量<<.
vav*_*ava 38
each只需通过数组并使用每个元素调用给定的块,这很简单.由于您在类中使用数组,因此您只需将each方法重定向到数组中的方法,即可快速轻松地读取/维护.
class Result
include Enumerable
def initialize
@results_array = []
end
def <<(val)
@results_array << val
end
def each(&block)
@results_array.each(&block)
end
end
r = Result.new
r << 1
r << 2
r.each { |v|
p v
}
#print:
# 1
# 2
Run Code Online (Sandbox Code Playgroud)
请注意,我已混入Enumerable.这将会给你一堆的排列方法,如all?,map等是免费的.
用Ruby来BTW你可以忘记继承.您不需要接口继承,因为duck-typing并不真正关心实际类型,并且您不需要代码继承,因为mixins对于这类事情更好.
小智 9
这感觉非常像c,我知道Ruby有更好的方法.
如果你想让一个对象"感觉"像一个数组,那么重写<<是一个好主意而且非常'Ruby'-ish.
但我不确定每种方法在引擎盖下真正做了什么.
Array的每个方法只循环遍历所有元素(我认为使用for循环).如果你想添加自己的每个方法(也非常'Ruby'-ish),你可以这样做:
def each
0.upto(@result_array.length - 1) do |x|
yield @result_array[x]
end
end
Run Code Online (Sandbox Code Playgroud)
小智 8
你的<<方法非常好,非常像Ruby.
要使类像数组一样,而不直接从Array继承,您可以混合使用Enumerable模块并添加一些方法.
这是一个例子(包括Chuck使用Forwardable的优秀建议):
# You have to require forwardable to use it
require "forwardable"
class MyArray
include Enumerable
extend Forwardable
def initialize
@values = []
end
# Map some of the common array methods to our internal array
def_delegators :@values, :<<, :[], :[]=, :last
# I want a custom method "add" available for adding values to our internal array
def_delegator :@values, :<<, :add
# You don't need to specify the block variable, yield knows to use a block if passed one
def each
# "each" is the base method called by all the iterators so you only have to define it
@values.each do |value|
# change or manipulate the values in your value array inside this block
yield value
end
end
end
m = MyArray.new
m << "fudge"
m << "icecream"
m.add("cake")
# Notice I didn't create an each_with_index method but since
# I included Enumerable it knows how and uses the proper data.
m.each_with_index{|value, index| puts "m[#{index}] = #{value}"}
puts "What about some nice cabbage?"
m[0] = "cabbage"
puts "m[0] = #{m[0]}"
puts "No! I meant in addition to fudge"
m[0] = "fudge"
m << "cabbage"
puts "m.first = #{m.first}"
puts "m.last = #{m.last}"
Run Code Online (Sandbox Code Playgroud)
哪个输出:
m[0] = fudge
m[1] = icecream
m[2] = cake
What about some nice cabbage?
m[0] = cabbage
No! I meant in addition to fudge
m.first = fudge
m.last = cabbage
Run Code Online (Sandbox Code Playgroud)
如果您创建一个继承自Array的类Results,您将继承所有功能.
然后,您可以通过重新定义它们来补充需要更改的方法,并且可以为旧功能调用super.
例如:
class Results < Array
# Additional functionality
def best
find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
delete(ininteresting_result)
super
end
end
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用内置库forwardable.如果您不能从Array继承,则此功能特别有用,因为您需要从另一个类继承:
require 'forwardable'
class Results
extend Forwardable
def_delegator :@result_array, :<<, :each, :concat # etc...
def best
@result_array.find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
@result_array.delete(ininteresting_result)
@result_array.compact
self
end
end
Run Code Online (Sandbox Code Playgroud)
在这两种形式中,您可以根据需要使用它:
r = Results.new
r << some_result
r.each do |result|
# ...
end
r.compact
puts "Best result: #{r.best}"
Run Code Online (Sandbox Code Playgroud)