Equivalent to Ruby Enumerable.collect that returns Enumerable?

In this code, I create an array of strings from "1" to "10000":

array_of_strings = (1..10000).collect {|i| String(i)} 

Does the Ruby Core API provide a way to enumerate an object that allows me to enumerate the same list by generating string values ​​on demand, rather than generating an array of strings?

Here is another example that hopefully clarifies what I'm trying to do:

 def find_me_an_awesome_username awesome_names = (1..1000000).xform {|i| "hacker_" + String(i) } awesome_names.find {|n| not stackoverflow.userexists(n) } end 

Where xform is the method I'm looking for. awesome_names is Enumerable, so xform does not create an array of strings of 1 million elements, but simply generates and returns strings of the form "hacker_ [N]" upon request.

By the way, here is how it looks in C #:

 var awesomeNames = from i in Range(1, 1000000) select "hacker_" + i; var name = awesomeNames.First((n) => !stackoverflow.UserExists(n)); 

(one solution)

Here is an extension for Enumerator that adds an xform method. It returns another enumerator that iterates over the values ​​of the original counter, with the conversion applied to it.

 class Enumerator def xform(&block) Enumerator.new do |yielder| self.each do |val| yielder.yield block.call(val) end end end end # this prints out even numbers from 2 to 10: (1..10).each.xform {|i| i*2}.each {|i| puts i} 
+7
ruby enumerable
source share
4 answers

Ruby 2.0 introduced Enumerable#lazy , which allows chains of map , select , etc ... and only generate final results at the end with to_a , first , etc. You can use it in any version of Ruby with require 'backports/2.0.0/enumerable/lazy' .

 require 'backports/2.0.0/enumerable/lazy' names = (1..Float::INFINITY).lazy.map{|i| "hacker_" + String(i) } names.first # => 'hacker_1' 

Otherwise, you can use Enumerator.new { with_a_block } . This is new in Ruby 1.9, so require 'backports/1.9.1/enumerator/new' if you need it in Ruby 1.8.x.

According to your example, the following will not create an intermediate array and will only build the necessary rows:

 require 'backports/1.9.1/enumerator/new' def find_me_an_awesome_username awesome_names = Enumerator.new do |y| (1..1000000).each {|i| y.yield "hacker_" + String(i) } end awesome_names.find {|n| not stackoverflow.userexists(n) } end 

You can even replace 100000 with 1.0 / 0 (i.e. infinity) if you want.

To answer your comment, if you always match your values ​​one to one, you might have something like:

 module Enumerable def lazy_each Enumerator.new do |yielder| each do |value| yielder.yield(yield value) end end end end awesome_names = (1..100000).lazy_each{|i| "hacker_#{i}"} 
+6
source share

It looks like you want an Enumerator object, but not really.

That is, the Enumerator object is an object that you can use to call next on demand (instead of each , which runs the entire loop). (Many use the language of internal and external iterators: each is internal, and Enumerator is external. You control it.)

Here's what the viewer looks like:

 awesome_names = Enumerator.new do |y| number = 1 loop do y.yield number number += 1 end end puts awesome_names.next puts awesome_names.next puts awesome_names.next puts awesome_names.next 

Here's a link to discuss more how you can use Enumerators lazily in Ruby: http://www.michaelharrison.ws/weblog/?p=163

There is also a section on this in the book by Pickax ("Programming Ruby" by Dave Thomas).

+1
source share
 class T < Range def each super { |i| yield String(i) } end end T.new(1,3).each { |s| ps } $ ruby rsc.rb "1" "2" "3" 

The next thing to do is return the Enumerator when called without a block ...

+1
source share
In the lists

there is every method:

 (1..100000).each 
0
source share

All Articles