2013-07-11 48 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