I need a FIFO list of a fixed size that can be set arbitrarily on object creation. An array works, but I'm tired of checking its size and popping off the oldest element every time I push a new value. I know people have said that subclassing Array is a bad idea, but I still want to do it as it's the most elegant solution to my need.
我需要一个固定大小的FIFO列表,可以在创建对象时任意设置。一个数组可以工作,但是我厌倦了检查它的大小并在每次推一个新值时取出最老的元素。我知道有人说过子类化数组是个坏主意,但我还是想这么做,因为它是我需要的最优雅的解决方案。
Here's my code so far.
这是我目前的代码。
class FIFOList < Array
attr_reader :FIFO_length
attr_writer :FIFO_length
def initialize(l)
super()
@FIFO_length = l
end
def push(element)
super(element)
self.shift if self.length > @FIFO_length
end
With this object, I can define a 25 element FIFO like so:
有了这个对象,我可以像这样定义一个25个元素FIFO:
a = FIFOList.new(25)
and push elements on it all day long and always have the most recent 25. As long as I'm pushing elements on one at a time (a.push()), all is good. However, I'd like to be able to initialize a new FIFO with an array, just like one can with a native Array object.
每天都在它上面推元素,总是有最近的25个。只要我一次把元素按在一个元素上(a。push()),一切都很好。但是,我希望能够使用数组来初始化一个新的FIFO,就像使用本机数组对象一样。
a = [1,2,3,4,5]
Which yields an array a, of 5 elements. But that's not how it's working and I'm not sure what to do about it. Here's an IRB session showing the problem:
它产生一个数组a,包含5个元素。但这不是它的工作方式,我不知道该怎么做。这是一个IRB会议显示的问题:
irb(main):008:0* a = FIFOList.new(5)
=> []
irb(main):009:0> a.class
=> FIFOList
irb(main):010:0> a = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):011:0> a.class
=> Array
So instead of the static array getting assigned as the "content" of the FIFOList object, "a" simply becomes a reference to the new static array. That's not the behavior I want. From what reading I've done it seems like maybe I need to add methods to my FIFOList class to override [] and possibly []=, but I'm not sure how to do that. If that's not the right approach, I'd be happy with a "load" method that takes an array as a parameter and does the magic internally, but I'm not sure how to "load" the passed values into the self. Can someone help?
因此,与将静态数组指定为FIFOList对象的“内容”不同,“a”只是成为新静态数组的引用。那不是我想要的行为。根据我所做的阅读,似乎我需要向我的FIFOList类添加方法来覆盖[]和[]=,但我不确定该怎么做。如果这不是正确的方法,我很乐意使用“load”方法,它将数组作为参数,并在内部执行魔法,但我不确定如何将传递的值“加载”到self中。有人可以帮忙吗?
4 个解决方案
#1
3
You already came up with your own solution, but here's mine:
你已经有了自己的解决方案,但这是我的:
class FIFOList < Array
attr_reader :fifo_length
def initialize(len, arr=[])
arr = arr[-len, len] if arr.size > len
super(arr)
@fifo_length = len
end
def push(*args)
if args.size > fifo_length
return replace(args[-fifo_length, fifo_length])
end
num_to_shift = (size + args.size) - fifo_length
shift(num_to_shift) unless num_to_shift < 0
super(*args)
end
end
a = FIFOList.new(5, ["a1", "a2", "a3"])
p a # => ["a1", "a2", "a3"]
p a.push("b1") # => ["a1", "a2", "a3", "b1"]
p a.push("c1", "c2", "c3") # => ["a3", "b1", "c1", "c2", "c3"]
p a.push("d1", "d2", "d3", "d4", "d5", "d6", "d7") # => ["d3", "d4", "d5", "d6", "d7"]
Note that the constructor will automatically cut the initial array if it's too long, and FIFOList#push
mirrors Array#push
in that it takes any number of arguments and always returns self
(and works correctly even if given too many arguments).
注意,如果初始数组太长,构造函数会自动删除它,并且FIFOList#push镜像数组#push,它接受任意数量的参数,并且总是返回self(即使给出太多参数,也能正确工作)。
Though some would object to monkey-patching Array, you could also add an Array#to_fifo
convenience method that creates a FIFOList:
虽然有些人会反对monkey-patching数组,但你也可以添加一个#to_fifo方便方法来创建一个FIFOList:
class Array
def to_fifo
FIFOList.new(size, self)
end
end
a = [1,2,3,4,5].to_fifo
p a.class # => FIFOList
You can see it working on repl.it: https://repl.it/@jrunning/WillingJoyfulAttributes
你可以看到它在使用repl。:https://repl.it/@jrunning WillingJoyfulAttributes
#2
3
You can do something very similar without inheriting from Array
, but using composition instead:
你可以在不继承数组的情况下做一些非常相似的事情,但是可以使用组合来代替:
class FIFOList
attr_reader :size, :arr
def self.[](*values)
obj = self.new(values.size)
obj.arr = values
obj
end
def initialize(size)
@size = size
@arr = Array.new
end
def push(element)
arr.push(element)
arr.shift if arr.length > size
arr
end
end
Then you can use it in a similar way:
然后你可以用类似的方式使用它:
a = FIFOList.new(3)
# => #<FIFOList:0x00007ffe87071150 @size=3, @arr=[nil, nil, nil]>
a.push 1
# => [nil, nil, 1]
a.push 2
# => [nil, 1, 2]
a.push 3
# => [1, 2, 3]
a.push 4
# => [2, 3, 4]
a.arr
# => [2, 3, 4]
Or if you want to use it without using push
for each value:
或者如果你想用它而不用推每一个值:
a = FIFOList[1,2,3]
# => #<FIFOList:0x00007feea9015d70 @size=3, @arr=[1, 2, 3]>
a.push 4
# => [2, 3, 4]
#3
1
You probably looking for Array#replace method.
您可能正在寻找数组#replace方法。
Replaces the contents of self with the contents of other_ary, truncating or expanding if necessary.
用他人的内容替换自我的内容,必要时进行截断或扩展。
a = FIFOList.new(5)
a.push(10) # now the content is [10]
a.replace([1,2,3,4,5]) # => now the content is [1,2,3,4,5]
#4
0
I still wish I could do straight assignment and have it work, but this code does allow me to optionally initialize.
我仍然希望我可以直接赋值并让它工作,但是这段代码允许我选择初始化。
class FIFOList < Array
attr_reader :FIFO_length
attr_writer :FIFO_length
def initialize(l, init_value = nil)
if init_value != nil && init_value.class.to_s == "Array" && init_value.length <= l then
super(init_value)
@FIFO_length = l
elsif init_value == nil
super()
@FIFO_length = l
else
raise "optional 2nd parameter required to be an Array"
end
end
def push(element)
super(element)
self.shift if self.length > @FIFO_length #truncate the FIFO to the defined length
end
end
#1
3
You already came up with your own solution, but here's mine:
你已经有了自己的解决方案,但这是我的:
class FIFOList < Array
attr_reader :fifo_length
def initialize(len, arr=[])
arr = arr[-len, len] if arr.size > len
super(arr)
@fifo_length = len
end
def push(*args)
if args.size > fifo_length
return replace(args[-fifo_length, fifo_length])
end
num_to_shift = (size + args.size) - fifo_length
shift(num_to_shift) unless num_to_shift < 0
super(*args)
end
end
a = FIFOList.new(5, ["a1", "a2", "a3"])
p a # => ["a1", "a2", "a3"]
p a.push("b1") # => ["a1", "a2", "a3", "b1"]
p a.push("c1", "c2", "c3") # => ["a3", "b1", "c1", "c2", "c3"]
p a.push("d1", "d2", "d3", "d4", "d5", "d6", "d7") # => ["d3", "d4", "d5", "d6", "d7"]
Note that the constructor will automatically cut the initial array if it's too long, and FIFOList#push
mirrors Array#push
in that it takes any number of arguments and always returns self
(and works correctly even if given too many arguments).
注意,如果初始数组太长,构造函数会自动删除它,并且FIFOList#push镜像数组#push,它接受任意数量的参数,并且总是返回self(即使给出太多参数,也能正确工作)。
Though some would object to monkey-patching Array, you could also add an Array#to_fifo
convenience method that creates a FIFOList:
虽然有些人会反对monkey-patching数组,但你也可以添加一个#to_fifo方便方法来创建一个FIFOList:
class Array
def to_fifo
FIFOList.new(size, self)
end
end
a = [1,2,3,4,5].to_fifo
p a.class # => FIFOList
You can see it working on repl.it: https://repl.it/@jrunning/WillingJoyfulAttributes
你可以看到它在使用repl。:https://repl.it/@jrunning WillingJoyfulAttributes
#2
3
You can do something very similar without inheriting from Array
, but using composition instead:
你可以在不继承数组的情况下做一些非常相似的事情,但是可以使用组合来代替:
class FIFOList
attr_reader :size, :arr
def self.[](*values)
obj = self.new(values.size)
obj.arr = values
obj
end
def initialize(size)
@size = size
@arr = Array.new
end
def push(element)
arr.push(element)
arr.shift if arr.length > size
arr
end
end
Then you can use it in a similar way:
然后你可以用类似的方式使用它:
a = FIFOList.new(3)
# => #<FIFOList:0x00007ffe87071150 @size=3, @arr=[nil, nil, nil]>
a.push 1
# => [nil, nil, 1]
a.push 2
# => [nil, 1, 2]
a.push 3
# => [1, 2, 3]
a.push 4
# => [2, 3, 4]
a.arr
# => [2, 3, 4]
Or if you want to use it without using push
for each value:
或者如果你想用它而不用推每一个值:
a = FIFOList[1,2,3]
# => #<FIFOList:0x00007feea9015d70 @size=3, @arr=[1, 2, 3]>
a.push 4
# => [2, 3, 4]
#3
1
You probably looking for Array#replace method.
您可能正在寻找数组#replace方法。
Replaces the contents of self with the contents of other_ary, truncating or expanding if necessary.
用他人的内容替换自我的内容,必要时进行截断或扩展。
a = FIFOList.new(5)
a.push(10) # now the content is [10]
a.replace([1,2,3,4,5]) # => now the content is [1,2,3,4,5]
#4
0
I still wish I could do straight assignment and have it work, but this code does allow me to optionally initialize.
我仍然希望我可以直接赋值并让它工作,但是这段代码允许我选择初始化。
class FIFOList < Array
attr_reader :FIFO_length
attr_writer :FIFO_length
def initialize(l, init_value = nil)
if init_value != nil && init_value.class.to_s == "Array" && init_value.length <= l then
super(init_value)
@FIFO_length = l
elsif init_value == nil
super()
@FIFO_length = l
else
raise "optional 2nd parameter required to be an Array"
end
end
def push(element)
super(element)
self.shift if self.length > @FIFO_length #truncate the FIFO to the defined length
end
end