Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby methods that either yield or return Enumerator

in recent versions of Ruby, many methods in Enumerable return an Enumerator when they are called without a block:

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

I want do do the same thing in my own methods like so:

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 }
like image 596
levinalex Avatar asked Aug 24 '11 23:08

levinalex


3 Answers

The core libraries insert a guard return to_enum(:name_of_this_method, arg1, arg2, ..., argn) unless block_given?. In your case:

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]
like image 103
tokland Avatar answered Nov 07 '22 13:11

tokland


use Enumerator#new:

class Array
  def double(&block)
    Enumerator.new do |y| 
      each do |x| 
        y.yield x*2 
      end 
    end.each(&block)
  end
end
like image 22
levinalex Avatar answered Nov 07 '22 13:11

levinalex


Another approach might be:

class Array
    def double(&block)
        map {|y| y*2 }.each(&block)
    end
 end
like image 2
mwolfetech Avatar answered Nov 07 '22 13:11

mwolfetech