Доступ к переданному блоку в Ruby - PullRequest
1 голос
/ 19 сентября 2011

У меня есть метод, который принимает блок, давайте назовем его внешним.Он в свою очередь вызывает метод, который принимает другой блок, вызывает его внутри.

То, что я хотел бы, чтобы внешний вызывал внутренний, передавая ему новый блок, который вызывает первый блок.

Вот конкретный пример:

class Array
  def delete_if_index
    self.each_with_index { |element, i| ** A function that removes the element from the array if the block passed to delete_if_index is true }
  end
end

['a','b','c','d'].delete_if_index { |i| i.even? }
  => ['b','d']

блок, переданный в delete_if_index, вызывается блоком, переданным в each_with_index.

Возможно ли это в Ruby и, в более широком смысле, насколько мы имеем доступ к блоку внутри функции, которая его получает?

Ответы [ 3 ]

4 голосов
/ 19 сентября 2011

Вы можете заключить блок в другой блок:

def outer(&block)
  if some_condition_is_true
    wrapper = lambda {
      p 'Do something crazy in this wrapper'
      block.call # original block
    }
    inner(&wrapper)
  else
    inner(&passed_block)
  end
end

def inner(&block)
  p 'inner called'
  yield
end

outer do
  p 'inside block'
  sleep 1
end

Я бы сказал, что открыть существующий блок и изменить его содержимое - «Делать это неправильно» TM , возможно, передача с продолжениемпоможет здесь?Также я бы с осторожностью обходил блоки с побочными эффектами;Я стараюсь сохранять лямбды детерминированными и иметь такие действия, как удаление чего-либо в теле метода.В сложном приложении это, вероятно, значительно упростит отладку.

1 голос
/ 19 сентября 2011

Возможно, пример выбран неправильно, но ваш конкретный пример такой же, как:

[1,2,3,4].reject &:even?

Открытие и изменение блока кажутся мне запахом кода. Было бы сложно написать так, чтобы побочные эффекты были очевидны.

Учитывая ваш пример, я думаю, что комбинация функций более высокого порядка сделает то, что вы хотите решить.

Обновление : Это не то же самое, как указано в комментариях. [1,2,3,4].reject(&:even?) смотрит на содержимое, а не на индекс (и возвращает [1,3], а не [2,4], как это было бы в вопросе). Нижеприведенный пример эквивалентен исходному примеру, но не сильно отличается.

[1,2,3,4].each_with_index.reject {|element, index| index.even? }.map(&:first)
0 голосов
/ 21 сентября 2011

Итак, вот решение моего собственного вопроса.Переданный блок неявно преобразуется в процесс, который можно получить с помощью синтаксиса & параметра.В этом случае proc существует внутри замыкания любого вложенного блока, так как он назначен локальной переменной в области видимости и может вызываться им:

class Array
  def delete_if_index(&proc)
    ary = []
    self.each_with_index { |a, i| ary << self[i] unless proc.call(i) }
    ary
  end
end

[0,1,2,3,4,5,6,7,8,9,10].delete_if_index {|index| index.even?}

  => [1, 3, 5, 7, 9]

Здесь блок преобразуется в proc и назначаетсяпеременной proc, которая затем доступна в блоке, передаваемом в each_with_index.

...