首页 > 解决方案 > 是否有内置方法来检查 #next 或 #peek 是否会引发 StopIteration?

问题描述

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

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

这通常工作正常,但这是在#next枚举已经被调用了几次之后。与此有关的问题是enum可能在最后并且为 传递了一些值enum,我遇到了由于完成而enum.peek引发的问题。有没有一种方法可以让我在打电话之前检查是否或会导致。例如,会有这种行为的东西?StopIterationenumenum.peekenum.nextStopIteration

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

标签: rubyenumerator

解决方案


您可以明确地,但也有这样的想法,即该方法通过简单地退出循环来rescue在内部挽救异常。(内部,与 具有相同的效果。)StopIterationloopStopIterationloopraise StopIterationbreak

当您尝试查看结尾时,此代码只会退出循环:

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

loop do
  print a.peek
  a.next
end

代码输出abcde. (它也透明地提出和拯救StopIteration。)

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

当然,一旦你窥视到最后,你就会被甩出循环。如果您不希望这样,您可以使用whileandrescue来自定义行为。例如,如果您想避免在查看结束时退出,并在使用 迭代结束时退出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

外部迭代器使用起来非常简单:next每次需要另一个元素时调用。当没有剩余元素时,next将引发StopIteration异常。这可能看起来不寻常——针对预期的终止条件而不是意外和异常事件引发异常。(StopIterationStandardErrorand的后代IndexError;请注意,它是唯一一个名称中没有“error”一词的异常类。) Ruby 在这种外部迭代技术中遵循 Python。通过将循环终止视为异常,它使您的循环逻辑非常简单;不需要检查返回值 next是否有特殊的迭代结束值,也不需要在调用之前调用某种 next?谓词next.


推荐阅读