ruby - 当您的类未定义#each 时,返回 Enumerator::Lazy 的最佳方法是什么?

标签 ruby lazy-evaluation enumerable ruby-2.0

Enumerable#lazy 依赖于提供 #each 方法的枚举。如果您的可枚举对象没有 #each 方法,您就不能使用 #lazy。现在 Kernel#enum_for#to_enum 提供了指定枚举方法的灵 active ,而不是 #each:

Kernel#enum_for(method = :each, *args)

但是 #enum_for 和 friend 们总是构建普通(非惰性)枚举器,而不是Enumerator::Lazy

我看到 Ruby 1.9.3 中的 Enumerator 提供了这种类似形式的#new:

Enumerator#new(obj, method = :each, *args)

不幸的是,构造函数在 Ruby 2.0 中已被完全删除。此外,我认为它在 Enumerator::Lazy 上根本不可用。所以在我看来,如果我有一个带有方法的类,我想为其返回一个惰性枚举器,如果该类没有 #each 那么我必须定义一些确实定义 #each.

例如,我有一个Calendar 类。从所有时间开始枚举每个日期对我来说真的没有意义。 #each 将毫无用处。相反,我提供了一种从开始日期开始(懒惰地)枚举的方法:

  class Calendar
    ...
    def each_from(first)
      if block_given?
        loop do
          yield first if include?(first)
          first += step
        end
      else
        EachFrom.new(self, first).lazy
      end
    end
  end

EachFrom 类如下所示:

class EachFrom
  include Enumerable
  def initialize(cal, first)
    @cal   = cal
    @first = first
  end
  def each
    @cal.each_from(@first) do |yielder, *vals|
      yield yielder, *vals
    end
  end
end

它有效,但感觉很重。也许我应该子类化 Enumerator::Lazy 并定义一个类似 Enumerator 中弃用的构造函数。你怎么看?

最佳答案

我认为你应该使用 to_enum 返回一个普通的 Enumerator:

class Calendar
  # ...
  def each_from(first)
    return to_enum(:each_from, first) unless block_given?
    loop do
      yield first if include?(first)
      first += step
    end
  end
end

这是大多数 ruby​​ist 所期望的。尽管它是一个无限的 Enumerable,但它仍然可用,例如:

Calendar.new.each_from(1.year.from_now).first(10) # => [...first ten dates...]

如果他们真的需要一个惰性枚举器,他们可以自己调用lazy:

Calendar.new.each_from(1.year.from_now)
  .lazy
  .map{...}
  .take_while{...}

如果你真的想要返回一个惰性枚举器,你可以从你的方法中调用lazy:

  # ...
  def each_from(first)
    return to_enum(:each_from, first).lazy unless block_given?
    #...

不过我不推荐它,因为它会出乎意料(IMO),可能会矫枉过正并且性能会降低。

最后,您的问题中存在一些误解:

  • Enumerable 的所有方法都假定一个each,而不仅仅是lazy

  • 如果您愿意,您可以定义一个需要参数的each 方法并包含EnumerableEnumerable 的大多数方法都不起作用,但 each_with_index 和其他几个方法将转发参数,以便立即可用。

  • 没有 block 的 Enumerator.new 消失了,因为 to_enum 是应该使用的。请注意, block 形式仍然存在。还有一个用于 Lazy 的构造函数,但它意味着从现有的 Enumerable 开始。

  • 您声明 to_enum 永远不会创建惰性枚举器,但这并不完全正确。 Enumerator::Lazy#to_enum 专门用于返回惰性枚举器。 Enumerable 上调用 to_enum 的任何用户方法都会使惰性枚举器保持惰性。

关于ruby - 当您的类未定义#each 时,返回 Enumerator::Lazy 的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15961453/

相关文章:

ruby - 将代码从 .erb 转换为 .haml 时遇到问题

javascript - 如何在rails中使用jquery删除特定项目?

ruby - 为什么 Enumerable 在 Ruby 中没有长度属性?

c - 在 Ruby C 扩展中实现链式迭代器

Ruby 删除可枚举列表中的重复项

ruby-on-rails - 在 Ruby on Rails 和 PostgreSQL 中使用 IN 运算符进行不区分大小写的搜索

ruby-on-rails - 如何从 Ruby on Rails 中的远程 JSON 文件的内容中读取单个数据?

ios - iOS 延迟初始化

haskell - Haskell 中的递归 IO

bash - Bash 中的惰性求值