Вызовите метод блока для итератора: each.magic.collect {...} - PullRequest
4 голосов
/ 07 апреля 2010

У меня есть класс с пользовательским методом each:

class CurseArray < Array
    def each_safe
        each do |element|
            unless element =~ /bad/
                yield element
            end
        end
    end 
end

И я хочу вызывать различные блочные методы, такие как "collect" или "inject" для этих повторяющихся элементов.Например:

curse_array.each_safe.magic.collect {|element| "#{element} is a nice sentence."}

Я знаю, что для этого есть специальная функция (которую я назвал «магией»), но я забыл.Пожалуйста помоги!: -)

Ответы [ 3 ]

6 голосов
/ 07 апреля 2010

Если метод дает результат, вам нужно передать ему блок. Нет способа определить блок, который автоматически проходит сам.

Самое близкое, что я могу получить к вашей спецификации, это:

def magic(meth)
  to_enum(meth)
end

def test
  yield 1 
  yield 2
end

magic(:test).to_a
# returns: [1,2]

Самый простой способ реализации вашего запроса:

class MyArray < Array 
  def each_safe 
    return to_enum :each_safe unless block_given? 
    each{|item| yield item unless item =~ /bad/}
  end 
end

a = MyArray.new 
a << "good"; a << "bad" 
a.each_safe.to_a
# returns ["good"] 
2 голосов
/ 07 апреля 2010

Как вы написали свой each_safe метод, самым простым будет

curse_array.each_safe { |element| do_something_with(element) }

Редактировать: О, ваш метод each_safe тоже не правильный. Это должно быть «каждый делает», а не «каждый.до»

Редактировать 2: Если вы действительно хотите иметь возможность делать такие вещи, как "each_safe.map", и в то же время иметь возможность делать "each_safe { ... }", вы можете написать свой метод как это:

require 'enumerator'

class CurseArray < Array
  BLACKLIST = /bad/
  def each_safe
    arr = []
    each do |element|
      unless element =~ BLACKLIST
        if block_given?
          yield element
        else
          arr << element
        end
      end
    end

    unless block_given?
      return Enumerator.new(arr)
    end
  end
end
0 голосов
/ 18 октября 2012

Выбранное решение использует общую идиому to_enum :method_name unless block_given?, которая в порядке, но есть альтернативы:

  1. Оставьте свой "недружелюбный" метод урожайности нетронутым, используйте enum_for при его вызове.

  2. Используйте ленивый Enumerator.

  3. Используйте ленивые массивы (нужно Ruby 2.0 или гем enumerable-lazy ).

Вот демонстрационный код:

class CurseArray < Array
  def each_safe
    each do |element|
      unless element =~ /bad/
        yield element
      end
    end
  end 

  def each_safe2
    Enumerator.new do |enum|
      each do |element|
        unless element =~ /bad/
          enum.yield element
        end
      end
    end
  end 

  def each_safe3
    lazy.map do |element|
      unless element =~ /bad/
        element
      end
    end.reject(&:nil?)
  end 
end

xs = CurseArray.new(["good1", "bad1", "good2"])
xs.enum_for(:each_safe).select { |x| x.length > 1 }
xs.each_safe2.select { |x| x.length > 1 }
xs.each_safe3.select { |x| x.length > 1 }.to_a
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...