Ruby - 优雅地比较两个枚举器

kik*_*ito 9 ruby enumerator

我在Ruby(1.9.2)中有两个来自两个不同来源(二进制数据)的长数据流.

这两个源以两个枚举器的形式封装.

我想检查两个流是否完全相同.

我有几个解决方案,但两者看起来都很不优雅.

第一个简单地将两者转换为数组:

def equal_streams?(s1, s2)
  s1.to_a == s2.to_a
end
Run Code Online (Sandbox Code Playgroud)

这是有效的,但它在内存方面并不是非常高效,特别是如果流有很多信息.

另一种选择是......呃.

def equal_streams?(s1, s2)
  s1.each do |e1|
    begin
      e2 = s2.next
      return false unless e1 == e2 # Different element found
    rescue StopIteration
      return false # s2 has run out of items before s1
    end
  end

  begin
    s2.next
  rescue StopIteration
    # s1 and s2 have run out of elements at the same time; they are equal
    return true
  end

  return false

end
Run Code Online (Sandbox Code Playgroud)

那么,有没有更简单,更优雅的方式呢?

saw*_*awa 10

假设您的流不包含元素,只需对您的代码进行轻微的重构:eof.

def equal_streams?(s1, s2)
  loop do
    e1 = s1.next rescue :eof
    e2 = s2.next rescue :eof
    return false unless e1 == e2
    return true if e1 == :eof
  end
end
Run Code Online (Sandbox Code Playgroud)

使用类似关键字loop应该比使用类似方法更快each.

  • 我非常喜欢这个.我没有想到我可以使用"守卫"值,例如你的例子中的:eof符号.谢谢! (2认同)

mu *_*ort 7

比较它们一次一个元素可能是你能做的最好的,但你可以做得比你的"呃"解决方案更好:

def grab_next(h, k, s)
  h[k] = s.next
rescue StopIteration
end

def equal_streams?(s1, s2)
  loop do
    vals = { }
    grab_next(vals, :s1, s1)
    grab_next(vals, :s2, s2)
    return true  if(vals.keys.length == 0)  # Both of them ran out.
    return false if(vals.keys.length == 1)  # One of them ran out early.
    return false if(vals[:s1] != vals[:s2]) # Found a mismatch.
  end
end
Run Code Online (Sandbox Code Playgroud)

棘手的部分是区分一个用完的流和两个用完的流.将StopIteration异常推入单独的函数并使用哈希中缺少键是一种相当方便的方法.vals[:s1]如果您的流包含falsenil只是检查是否存在密钥解决该问题,只是检查将导致问题.

  • @kikito:这很酷,我也赞同了sawa的回答.Sawa和我正在使用几乎相同的逻辑,我只是​​使用缺少哈希键来表示结束而不是[sentinel value](http://en.wikipedia.org/wiki/Sentinel_value).如果你知道你的流中永远不会有`:eof`符号,那么就不需要额外的机器来避免使用哨兵了. (2认同)