Пропустить блок, переданный методу другому методу в Ruby

Я пытаюсь написать клон методов массива ruby ​​ keep_if и delete_if. Вот мой код.

module Strain
  def keep
    self.inject([]) do |extracts, element|
      yield(element) ? extracts << element : extracts 
    end
  end

  def discard
    self.inject([]) do |extracts, element|
      !yield(element) ? extracts << element : extracts
    end
  end
end

class Array
  include Strain
end

Это работает. Но я хочу сделать что-то вроде:

def discard
  self - self.keep &block
end

Желаемое поведение:

[1, 2, 3].discard { |number| number < 2 }
# => [2, 3]

Поэтому мне нужно передать блок, который передается методу discard, который будет передан методу keep. Как достичь этого?

Ответ 1

Вы можете явно ссылаться на блок

def discard(&block)
  self - self.keep(&block)
end

или неявно

def discard
  self - self.keep(&Proc.new {})
end

В вашем случае я бы предложил первый подход.

Ответ 2

Во втором примере &Proc.new {} не пропускает блок, он создает новый пустой. Следует опустить {} и написать его как self.keep(&Proc.new) или просто keep(&proc), так как self. является избыточным, а proc является рекомендуемым синонимом для Proc.new:

# passes on the block or the absence of a block
def discard(&block)
  self - keep(&block)
end

# passes on the block and fails if no block given
def discard
  self - keep(&proc)
end

И Proc.new, и proc без блока используют блок текущего метода.

&proc потерпит неудачу, если discard не получит блок. Поэтому первый пример является лучшим, если вы хотите пропустить блок или отсутствие блока (&nil вообще не пропускает блок). Второй пример (как я его изменил) является лучшим, если отсутствующий блок является ошибкой.

В обоих случаях каждый раз, когда вызывается метод discard, создается новый объект Proc, который не является бесплатным.