返回Enumerable的Ruby Enumerable.collect的等价物?

mac*_*nir 8 ruby enumerable

在这段代码中,我创建了一个字符串"1"到"10000"的数组:

array_of_strings = (1..10000).collect {|i| String(i)}
Run Code Online (Sandbox Code Playgroud)

Ruby Core API是否提供了一种获取可枚举对象的方法,该对象允许我枚举相同的列表,根据需要生成字符串值,而不是生成字符串数组?

这是另一个例子,希望澄清我想要做的事情:

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

xform我正在寻找的方法在哪里.awesome_names是一个Enumerable,因此xform不是创建一个100万个元素的字符串数组,而只是根据需要生成和返回"hacker_ [N]"形式的字符串.

顺便说一句,这就是它在C#中的样子:

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

(一种解决方案)

这是Enumerator的扩展,它添加了一个xform方法.它返回另一个枚举器,它迭代原始枚举器的值,并应用了一个变换.

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}
Run Code Online (Sandbox Code Playgroud)

Mar*_*une 6

推出红宝石2.0 Enumerable#lazy,它允许一个链map,select等等......,只有在产生与最终的最终结果to_a,first等...您可以在任何Ruby版本使用它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'
Run Code Online (Sandbox Code Playgroud)

否则,你可以使用Enumerator.new { with_a_block }.它是Ruby 1.9中的新功能,所以require 'backports/1.9.1/enumerator/new'如果你需要它在Ruby 1.8.x中.

根据您的示例,以下内容不会创建中间数组,只会构造所需的字符串:

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
Run Code Online (Sandbox Code Playgroud)

如果需要,您甚至可以将100000替换为1.0/0(即无穷大).

要回答您的评论,如果您始终将您的值一对一映射,您可以使用以下内容:

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}"}
Run Code Online (Sandbox Code Playgroud)