当您的类未定义#each时,返回Enumerator :: Lazy的最佳方法是什么? [英] What's the best way to return an Enumerator::Lazy when your class doesn't define #each?

查看:76
本文介绍了当您的类未定义#each时,返回Enumerator :: Lazy的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Enumerable#lazy依赖于您提供的#each方法.如果您的可枚举没有#each方法,则不能使用#lazy.现在,Kernel#enum_for#to_enum可以灵活地指定#each以外的枚举方法:

Enumerable#lazy relies on your enumerable providing an #each method. If your enumerable doesn't have an #each method you can't use #lazy. Now Kernel#enum_for and #to_enum provide the flexibility to specify an enumeration method other than #each:

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

但是#enum_for和朋友总是构造普通的(非惰性)枚举器,而不是Enumerator::Lazy.

But #enum_for and friends always construct plain (non-lazy) enumerators, never Enumerator::Lazy.

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

I see that Enumerator in Ruby 1.9.3 offers this similar form of #new:

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

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

Unfortunately that constructor has been completely removed in Ruby 2.0. Also I don't think it was ever available at all on Enumerator::Lazy. So it seems to me that if I have a class with a method I want to return a lazy enumerator for, if that class has no #each then I have to define some helper class that does define #each.

例如,我有一个Calendar类.对我来说,提供从所有时间开始的每个日期的枚举对我来说真的没有任何意义. #each将是无用的.相反,我提供了一种从开始日期开始(延迟)枚举的方法:

For instance, I've got a Calendar class. It doesn't really make sense for me to offer to enumerate every date from the beginning of all time. An #each would be useless. Instead I offer a method that enumerates (lazily) from a starting date:

  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弃用的构造函数.你觉得呢?

It works but it feels heavy. Maybe I should subclass Enumerator::Lazy and define a constructor like that deprecated one from Enumerator. What do you think?

推荐答案

我认为您应该使用to_enum返回正常的Enumerator:

I think you should return a normal Enumerator using to_enum:

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

这是大多数红宝石学家所期望的.即使它是无限的Enumerable,它仍然可以使用,例如:

This is what most rubyists would expect. Even though it's an infinite Enumerable, it is still usable, for example:

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

如果他们实际上需要一个惰性枚举器,则可以自己调用lazy:

If they actually need a lazy enumerator, they can call lazy themselves:

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

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

If you really want to return a lazy enumerator, you can call lazy from you method:

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

不过,我不建议这样做,因为这是意外的(IMO),可能会造成过大的杀伤力,并且性能会降低.

I would not recommend it though, since it would be unexpected (IMO), could be an overkill and will be less performant.

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

Finally, there are a couple of misconceptions in your question:

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

  • All methods of Enumerable assume an each, not just lazy.

如果愿意,可以定义一个需要参数的each方法,并包含Enumerable. Enumerable的大多数方法都行不通,但是each_with_index和其他一些方法会转发参数,因此可以立即使用.

You can define an each method that requires a parameter if you like and include Enumerable. Most methods of Enumerable won't work, but each_with_index and a couple of others will forward arguments so these would be usable immediately.

没有Enumerator.new消失了,因为to_enum是应该使用的.注意,块形式仍然存在. Lazy也有一个构造函数,但这是从现有的Enumerable开始的.

The Enumerator.new without a block is gone because to_enum is what one should use. Note that the block form remains. There's also a constructor for Lazy, but it's meant to start from an existing Enumerable.

您声明to_enum从未创建过惰性枚举器,但这并非完全正确. Enumerator::Lazy#to_enum专门用于返回惰性枚举器. Enumerable上任何调用to_enum的用户方法都将使惰性枚举数保持惰性.

You state that to_enum never creates a lazy enumerator, but that's not entirely true. Enumerator::Lazy#to_enum is specialized to return a lazy enumerator. Any user method on Enumerable that calls to_enum will keep a lazy enumerator lazy.

这篇关于当您的类未定义#each时,返回Enumerator :: Lazy的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆