8

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

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

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

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));

(一种解决方案)

这是 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}
4

4 回答 4

6

引入的 Ruby 2.0Enumerable#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'

否则,您可以使用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

如果需要,您甚至可以将 100000 替换为 1.0/0(即 Infinity)。

要回答您的评论,如果您总是一对一地映射您的值,您可能会有类似的内容:

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}"}
于 2010-02-24T18:29:21.733 回答
1

听起来您想要一个 Enumerator 对象,但不完全是。

也就是说,一个 Enumerator 对象是一个可以用来next按需调用的对象(而不是each整个循环)。(许多人使用内部迭代器与外部迭代器的语言:each是内部的,而枚举器是外部的。你驱动它。)

以下是枚举器的外观:

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

这是一个链接,更多讨论如何在 Ruby 中懒惰地使用枚举器:http: //www.michaelharrison.ws/weblog/? p=163

Pickaxe 书(Dave Thomas编写的Programming Ruby )中也有关于此的部分。

于 2010-02-24T17:28:50.787 回答
1
class T < Range
  def each
    super { |i| yield String(i) }
  end
end

T.new(1,3).each { |s| p s }
$ ruby rsc.rb
"1"
"2"
"3"

接下来要做的是在没有块的情况下调用时返回一个枚举器......

于 2010-02-24T17:46:29.403 回答
0

列表有一个 each 方法:

(1..100000).each
于 2010-02-24T16:57:48.890 回答