Ruby:Time响应succ方法却无法在Range#each中迭代的问题
Ruby Range可安全迭代的判断逻辑
Ruby官方文档对Range#each的触发条件做了简化说明,实际可迭代的完整判断逻辑如下:
- 正向迭代场景下范围不能为反向区间,即起点值不能大于终点值,否则
each会直接返回空不执行迭代 - 范围起点需要响应
succ方法 - 起点对象的类型不能是Ruby内部硬编码禁止迭代的类型,目前这类类型包括
Time、DateTime等时间类型,即便它们实现了succ方法也无法正常迭代
你遇到的问题是因为CRuby在Range#each的底层实现中,对Time类型做了直接拦截:只要检测到范围起点是Time类型,就会直接抛出TypeError (can't iterate from Time),不会触发succ方法的调用。而Time#succ仅为旧版本兼容保留,自Ruby 1.9.2起就已标记废弃。
如果要判断一个Range是否可以安全调用each,最可靠的方式是结合快速校验和异常兜底:
def iterable_range?(range) # 快速过滤明确不可迭代的场景 return false unless range.begin.respond_to?(:succ) return false if [Time, DateTime, Float].include?(range.begin.class) # 兜底校验,尝试迭代一次判断是否报错 begin range.each { break } true rescue TypeError false end end
如果需要迭代时间区间,不要使用Range#each,改用step方法指定迭代步长即可:
t = Time.now range = (t..t+10) # 按1秒为步长迭代时间区间 range.step(1) { |current_time| p current_time }
内容的提问来源于stack exchange,提问作者anon_swe
相关产品推荐
相关产品推荐

