2010-02-24 74 views
7

在这段代码中,我创建的字符串“1”到“10000”的数组:等价的返回Enumerable的Ruby Enumerable.collect?

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

是否Ruby的核心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)); 

(一体化解决方案)

这里是一个扩展枚举增加了一个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} 
+0

......应该读“2至20” – mackenir 2010-02-24 21:00:17

回答

6

的Ruby 2.0引入了Enumerable#lazy它允许一个链mapselect,等等,只有产生最后的结果与to_a,first等结尾......您可以在任何带有require 'backports/2.0.0/enumerable/lazy'的Ruby版本中使用它。

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中是新的,所以如果你在Ruby 1.8.x中需要它,那么require 'backports/1.9.1/enumerator/new'

按照您的例子,下面将不能创建一个中间数列,只会构建所需的字符串:

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 

你甚至可以10​​0000通过更换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}"} 
+0

@marc,这看起来像它!你知道我怎么可能把这个模式变成一个更加简洁的可重用的方法,采用'可枚举的东西'和'变换函数'? 在这个例子中,'transformer function'是'{| i | “hacker_”+ String(i)}','enumerable thing'将是'(1..100000)'或其他。 – mackenir 2010-02-24 18:47:43

+0

非常感谢。我更新了我的问题,其中提供了一个可能的实现方案,我可以从您的答案中找到答案,也可以阅读@ Telemachus发布的链接,然后我发现* * *更新:)再次,谢谢! – mackenir 2010-02-24 20:58:27

+0

@mackenir:Enumerable转换函数是'map'。你应该能够将'each'改成'map',并保持算法不变。 – Chuck 2010-02-24 21:06:20

0

名单有每个方法:

(1..100000).each 
+1

......好吧,那就继续吧。 :) – mackenir 2010-02-24 17:00:02

+1

...好的,现在你开始搜索Ruby迭代。 – Geo 2010-02-24 17:01:35

+0

但是你的代码只是迭代整数范围。它不会生成新的字符串枚举。请尝试把自己放在我的白痴鞋:)。 – mackenir 2010-02-24 17:11:39

1

这听起来像你想要一个枚举对象,但不完全是。

也就是说,枚举器对象是一个对象,您可以使用该对象根据需要调用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

还有关于这方面的书镐一节(编程红宝石由戴夫·托马斯)。

+0

谢谢。嗯。当找到匹配的元素时,肯定会停止枚举。您可以通过在很大范围内运行查找来确认这一点,谓词'假'和'真'后者立即返回。如果两者都列举了他们都会在同一时间返回的所有内容。 回复:下一个枚举,我试图找到'enumerable转换'设施,以编写更多简洁,声明代码,并手动枚举不会真的实现这一点。 也许答案只是实现它。 – mackenir 2010-02-24 17:36:55

+0

随着所有的CRs被删除,这是不易理解的。我的意思是,(1..1000000000000000000).find {| i | true}很快,(1..1000000000000000000).find {| i | false}很慢。意思发现只是枚举,直到它“发现”。 – mackenir 2010-02-24 17:40:43

+0

有用的链接 - 我想我明白了,它帮助回答了这个问题。 – mackenir 2010-02-24 20:59:18

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" 

接下来要做的事情就是返回时不使用块称为枚举...