методы ruby, которые либо выдают, либо возвращают Enumerator

в последних версиях Ruby многие методы в Enumerable возвращают Enumerator при вызове без блока:

[1,2,3,4].map 
#=> #<Enumerator: [1, 2, 3, 4]:map> 
[1,2,3,4].map { |x| x*2 }
#=> [2, 4, 6, 8] 

Я хочу сделать то же самое в своих собственных методах, например:

class Array
  def double(&block)
    # ???
  end
end

arr = [1,2,3,4]

puts "with block: yielding directly"
arr.double { |x| p x } 

puts "without block: returning Enumerator"
enum = arr.double
enum.each { |x| p x }

person levinalex    schedule 24.08.2011    source источник


Ответы (4)


Основные библиотеки вставляют защиту return to_enum(:name_of_this_method, arg1, arg2, ..., argn) unless block_given?. В твоем случае:

class Array
  def double
    return to_enum(:double) unless block_given?
    each { |x| yield 2*x }
  end
end

>> [1, 2, 3].double { |x| puts(x) }
2
4
6 
>> ys = [1, 2, 3].double.select { |x| x > 3 } 
#=> [4, 6]
person tokland    schedule 06.08.2013
comment
аккуратный. не знал об этом. - person levinalex; 07.08.2013
comment
Небольшое примечание: иногда вашей функции необходимо принимать параметры, поэтому использование to_enum только с :my_method не сработает (поскольку, когда перечисляемое будет перечисляться, ваша функция будет вызываться без параметров). Например, если пример здесь был def mult_by(factor) ... end, вам нужно было бы использовать to_enum(:my_method, factor). - person avivr; 06.01.2014

используйте Enumerator#new:

class Array
  def double(&block)
    Enumerator.new do |y| 
      each do |x| 
        y.yield x*2 
      end 
    end.each(&block)
  end
end
person levinalex    schedule 09.02.2012
comment
Спасибо - использование .each(&block) было именно тем, что я искал - person Greg; 15.05.2016

Другой подход может быть:

class Array
    def double(&block)
        map {|y| y*2 }.each(&block)
    end
 end
person mwolfetech    schedule 25.08.2011
comment
В этом суть, но, в отличие от ответа @levinalex, это решение запускает карту по всему массиву до того, как какой-либо потребитель вызовет каждый. - person Dean Radcliffe; 22.10.2013

самый простой способ для меня

class Array
  def iter
      @lam = lambda {|e| puts e*3}
      each &@lam
  end
end

array = [1,2,3,4,5,6,7]
array.iter

=> 3 6 9 12 15 18 21

person DDD    schedule 20.12.2016