3

在 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? }

…但当然,即使第一个编码有效,它也会继续读取每个编码的文件。

是否有一个简单的模式可以正常工作,但在找到第一次成功后不会继续读取文件?

4

4 回答 4

4

如果你lazy在其中撒上 a ,map只会消耗数组中那些被使用的元素find- 即一旦find停止,map也会停止。所以这会做你想要的:

possible_reads = encodings.lazy.map {|e| IO.read(f, "r:#{e}")}
contents = possible_reads.find {|s| s.valid_encoding? }
于 2013-07-11T22:42:46.443 回答
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
于 2013-07-11T23:20:06.853 回答
1

您想使用break语句:

contents = encodings.each do |e|

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

end
于 2016-07-20T02:27:44.420 回答
0

我能想到的最好的办法就是和我们的好朋友一起inject

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

这仍然不是最理想的,因为它在找到匹配项后继续循环遍历编码,尽管它对它们没有做任何事情,所以它是一个小丑。大多数丑陋来自......好吧,代码本身。:/

于 2013-07-11T22:32:59.747 回答