2013-07-11 55 views
2

在Ruby第一值的映射,我有簡單的值的陣列(可能的編碼):功能發現通過測試

encodings = %w[ utf-8 iso-8859-1 macroman ] 

我想保持從磁盤讀取一個文件,直到結果是有效的。我可以這樣做:

good = encodings.find{ |enc| IO.read(file, "r:#{enc}").valid_encoding? } 
contents = IO.read(file, "r:#{good}") 

...但當然這是愚蠢的,因爲它讀取文件兩次爲良好的編碼。我可以像程序風格那樣編程它:

contents = nil 
encodings.each do |enc| 
    if (s=IO.read(file, "r:#{enc}")).valid_encoding? 
    contents = s 
    break 
    end 
end 

但我想要一個功能性解決方案。我可以在功能上這樣做:

contents = encodings.map{|e| IO.read(f, "r:#{e}")}.find{|s| s.valid_encoding? } 

...但當然,即使第一個有效,每個編碼都會繼續讀取文件。

是否有一個簡單的功能模式,但在找到第一個成功後不會繼續讀取文件?

+0

你的意思是'find'? '第一個'沒有擋住。 – sepp2k

+0

@ sepp2k Bah,當然,我做到了。謝謝。我已編輯修復。 – Phrogz

回答

4

如果撒在那裏一個lazymap只會消耗由find使用的陣列的那些元件 - 即一旦find停止時,停止map以及。因此,這會做你想要什麼:

possible_reads = encodings.lazy.map {|e| IO.read(f, "r:#{e}")} 
contents = possible_reads.find {|s| s.valid_encoding? } 
+0

'NoMethodError:undefined method'lazy'for#' – Phrogz

+0

@Progrog它是2.0中的新功能。在早期版本中,您可以使用'backports' gem或者只是使用您的代碼(或者如果這是一個選項,則升級)。 – sepp2k

+0

啊;我以爲我在1.9.3文檔中看到它。你的回答很優雅,但是我會打開這一天,以防另一種解決辦法,而不是懶惰。 – Phrogz

0

我能想出的最好的是我們的好朋友inject

contents = encodings.inject(nil) do |s,enc| 
    s || (c=File.open(f,"r:#{enc}").valid_encoding? && c 
end 

這仍然是次優的,因爲它通過編碼找到匹配後繼續循環,雖然它不會做任何事情與他們在一起,所以這是一個小丑。大多數醜陋來自......呃,代碼本身。 :/

1

跳頻上sepp2k的回答是:如果你不能用2.0的,懶惰的枚舉可以很容易地在1.9實現:

class Enumerator 

    def lazy_find 
    self.class.new do |yielder| 
     self.each do |element| 
     if yield(element) 
      yielder.yield(element) 
      break 
     end 
     end 
    end 
    end 

end 

a = (1..100).to_enum 
p a.lazy_find { |i| i.even? }.first 

# => 2 
+1

該方法應該被稱爲'lazy_select',因爲它可以做'選擇'(但是延遲)。然而,執行'lazy_select {...}。'只是做與'find'相同的事情。對於Phrogz想要的,你需要'lazy_map',而不是'lazy_select'。 – sepp2k

+0

這只是一個例子,很容易推斷這個任何惰性風味的枚舉。另外,#select會返回多個匹配項; #find僅返回第一個匹配項。可枚舉鏈末尾的#first調用只是爲了從可枚舉值中獲取該值。 – Catnapper

+1

對於這個問題,不需要使用'Enumerator':'Enumerable; def find_mapped;每個{| O |如果v = yield(o)則返回v end};結束; end'然後是'contents = encodings.find_mapped {| enc | (S = IO.read(F, 「R:#{ENC}」))valid_encoding。? && s}' – Phrogz

1

您要使用的break聲明:

contents = encodings.each do |e| 

    s = IO.read(f, "r:#{e}") 
    s.valid_encoding? and break s 

end