Создайте FIFO фиксированной длины из массива в Ruby - PullRequest
0 голосов
/ 07 мая 2018

Мне нужен список FIFO фиксированного размера, который можно произвольно установить при создании объекта. Массив работает, но мне надоело проверять его размер и выталкивать самый старый элемент каждый раз, когда я нажимаю новое значение. Я знаю, что люди говорили, что создание подклассов Array - плохая идея, но я все еще хочу сделать это, так как это самое элегантное решение для моих нужд.

Вот мой код.

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

С помощью этого объекта я могу определить FIFO из 25 элементов следующим образом:

a = FIFOList.new(25)

и вставлять элементы в течение всего дня и всегда иметь самые последние 25. Пока я нажимаю элементы по одному за раз (a.push ()), все хорошо. Тем не менее, я хотел бы иметь возможность инициализировать новый FIFO с массивом, как это можно сделать с собственным объектом Array.

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

Что дает массив 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

Таким образом, вместо статического массива, назначаемого в качестве «содержимого» объекта FIFOList, «a» просто становится ссылкой на новый статический массив. Это не то поведение, которое я хочу. Из того, что я прочитал, кажется, что, возможно, мне нужно добавить методы в мой класс FIFOList для переопределения [] и, возможно, [] =, но я не уверен, как это сделать. Если это неправильный подход, я был бы доволен методом «загрузки», который принимает массив в качестве параметра и выполняет магию внутри, но я не уверен, как «загрузить» переданные значения в себя. Может кто-нибудь помочь?

Ответы [ 4 ]

0 голосов
/ 07 мая 2018

Вы можете сделать что-то очень похожее, не наследуя от 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]
0 голосов
/ 07 мая 2018

Я все еще хотел бы выполнить прямое назначение и заставить его работать, но этот код позволяет мне при необходимости инициализировать.

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
0 голосов
/ 07 мая 2018

Вы уже придумали собственное решение, но вот мое:

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 (и работает правильно, даже если задано слишком много аргументов ).

Хотя некоторые могут возразить, что Monkey-patching Array, вы также можете добавить Array#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

Вы можете видеть, что он работает на repl.it: https://repl.it/@jrunning/WillingJoyfulAttributes

0 голосов
/ 07 мая 2018

Вы, вероятно, ищете метод Array # replace .

Заменяет содержимое self содержимым other_ary, укорачивая или расширяя при необходимости.

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]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...