【问题标题】:Is there a built-in way to check if #next or #peek will raise StopIteration?是否有内置方法来检查 #next 或 #peek 是否会引发 StopIteration?
【发布时间】:2020-01-17 16:46:49
【问题描述】:

我正在使用一些迭代器,我必须按照这些思路做一些事情(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

【问题讨论】:

    标签: ruby enumerator


    【解决方案1】:

    您可以显式地rescueStopIteration,但也有一种想法是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 的书Ruby 编程语言

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

    【讨论】:

      猜你喜欢
      • 2011-09-10
      • 2020-01-26
      • 2021-01-28
      • 2015-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多