在Ruby中用数组创建一个固定长度的FIFO

时间:2018-05-07 18:36:55

标签: arrays ruby

我需要一个固定大小的FIFO列表,可以在创建对象时任意设置。一个数组工作,但我厌倦了检查它的大小,并在每次推送一个新值时弹出最旧的元素。我知道有人说子类化Array是一个坏主意,但我仍然想这样做,因为它是我需要的最优雅的解决方案。

到目前为止,这是我的代码。

footer {
    width: 100vw;
}

有了这个对象,我可以像这样定义一个25元素的FIFO:

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

并且整天推送元素并且总是拥有最新的25个。只要我一次推送一个元素(a.push()),一切都很好。但是,我希望能够使用数组初始化一个新FIFO,就像使用本机Array对象一样。

a = FIFOList.new(25)

产生5个元素的数组a。但这不是它的工作方式,我不知道该怎么做。这是一个显示问题的IRB会话:

a = [1,2,3,4,5]

因此,静态数组不会被指定为&#34;内容&#34; FIFOL对象,&#34; a&#34;只是成为新静态数组的引用。那不是我想要的行为。从我所做的阅读中看来,似乎我需要在我的FIFOList类中添加方法来覆盖[]和可能的[] =,但我不知道该怎么做。如果这不是正确的方法,我会对#34;加载&#34;感到满意。将数组作为参数并在内部完成魔法的方法,但我不确定如何加载&#34;传递给自我的价值观。有人可以帮忙吗?

4 个答案:

答案 0 :(得分:3)

你已经想出了自己的解决方案,但这是我的:

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"]

请注意,构造函数会自动剪切初始数组,如果它太长,FIFOList#push镜像Array#push,因为它需要任意数量的参数并始终返回self(并且正常工作)即使给出了太多的论据)。

虽然有些人会反对猴子修补数组,但你也可以添加一个创建FIFOList的Array#to_fifo便利方法:

class Array
  def to_fifo
    FIFOList.new(size, self)
  end
end

a = [1,2,3,4,5].to_fifo
p a.class # => FIFOList

您可以在repl.it上查看它:https://repl.it/@jrunning/WillingJoyfulAttributes

答案 1 :(得分:3)

您可以在不继承Array的情况下执行非常类似的操作,但使用撰写代替:

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

然后你可以用类似的方式使用它:

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]

或者如果您想在不使用push每个值的情况下使用它:

a = FIFOList[1,2,3]
# => #<FIFOList:0x00007feea9015d70 @size=3, @arr=[1, 2, 3]>
a.push 4
# => [2, 3, 4]

答案 2 :(得分:1)

您可能正在寻找Array#replace方法。

  

将self的内容替换为other_ary的内容,必要时截断或展开。

sortColumnName

答案 3 :(得分:0)

我仍然希望我可以直接分配并使其正常工作,但此代码允许我选择初始化。

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