是否有内置方法来检查#next 或#peek 是否会引发 StopIteration?

Is there a built-in way to check if #next or #peek will raise StopIteration?

我正在使用一些迭代器,我必须按照这些思路做一些事情(enum 是一个枚举器)

enums_with_zero << enum.rewind if enum.peek == 0

通常工作正常,但这是在 #next 已经对枚举调用了几次之后。这个问题是 enum 可能在最后并且为 enum 传递了一些值,我遇到了 enum.peek 引发 StopIteration 的问题,因为 enum完成。有没有一种方法可以让我在调用它之前检查 enum.peekenum.next 是否会导致 StopIteration。例如,会有这种行为的东西?

class Enumerator
  def has_next?
    begin
      peek && true
    rescue StopIteration
      false
    end
  end
end

您可以 rescue 显式 StopIteration,但也有这样的想法,即 loop 方法通过简单地退出循环在内部挽救 StopIteration 异常。 (loop里面,raise StopIterationbreak的效果是一样的。)

当您试图查看结束时,这段代码只是简单地退出循环:

a = %w(a b c d e).to_enum

loop do
  print a.peek
  a.next
end

代码输出abcde。 (还透明养救StopIteration。)

因此,如果您想在尝试查看末尾时简单地忽略 StopIteration 异常,只需使用 loop

当然,一旦你看到最后,你就会被甩出循环。如果您不想这样,您可以使用 whilerescue 来自定义行为。例如,如果你想避免在看到结束时退出,并在使用 next 迭代结束时退出,你可以这样做:

a = %w(a b c d e).to_enum

while true
  begin  
    print a.peek
  rescue StopIteration
    print "\nTried to peek past the end of the enum.\nWe're gonna overlook that.\n"
  end
  x = a.next rescue $!
  break if x.class == StopIteration
end

p 'All done!'

循环中的最后两行与此相同,您可以改用:

begin
  a.next
rescue StopIteration
  break
end

要说明的一点是,处理 StopIteration 是 Ruby 处理到达迭代器末尾的预期方式。引自 Matz 的书 The Ruby Programming Language:

External iterators are quite simple to use: just call next each time you want another element. When there are no more elements left, next will raise a StopIteration exception. This may seem unusual—an exception is raised for an expected termination condition rather than an unexpected and exceptional event. (StopIteration is a descendant of StandardError and IndexError; note that it is one of the only exception classes that does not have the word “error” in its name.) Ruby follows Python in this external iteration technique. By treating loop termination as an exception, it makes your looping logic extremely simple; there is no need to check the return value of next for a special end-of-iteration value, and there is no need to call some kind of next? predicate before calling next.