2010-02-24 64 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" 

接下來要做的事情就是返回時不使用塊稱爲枚舉...