在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 :(得分:4)
如果你在那里撒lazy
,map
只会消耗find
使用的数组元素 - 即find
停止map
也停止了。所以这将做你想要的:
possible_reads = encodings.lazy.map {|e| IO.read(f, "r:#{e}")}
contents = possible_reads.find {|s| s.valid_encoding? }
答案 1 :(得分: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
答案 2 :(得分:1)
答案 3 :(得分:0)
我能想到的最好的是与我们的好朋友inject
:
contents = encodings.inject(nil) do |s,enc|
s || (c=File.open(f,"r:#{enc}").valid_encoding? && c
end
这仍然是次优的,因为它在找到匹配后继续循环编码,虽然它对它们没有任何作用,所以这是一个小丑。大多数丑陋来自......好吧,代码本身。 :/