【问题标题】:How can I make a ruby enumerator that does lazy iteration through two other enumerators?如何制作一个通过其他两个枚举器进行惰性迭代的 ruby​​ 枚举器?
【发布时间】:2016-12-16 00:24:43
【问题描述】:

假设我有两个枚举器,enum1enum2,它们必须被延迟迭代(因为它们有副作用)。如何构造第三个枚举器enum3,其中enum3.each{|x| x}懒惰地 返回enum1 + enum2 的等价物?

在我的实际用例中,我在两个文件中进行流式传输,并且需要流式传输连接。

【问题讨论】:

    标签: ruby enumerator lazy-sequences


    【解决方案1】:

    这似乎正是我想要的方式;

    enums.lazy.flat_map{|enum| enum.lazy }
    

    这是演示。定义这些产生副作用的方法;

    def test_enum
      return enum_for __method__ unless block_given?
      puts 'hi'
      yield 1
      puts 'hi again'
      yield 2
    end  
    
    def test_enum2
      return enum_for __method__ unless block_given?
      puts :a
      yield :a
      puts :b
      yield :b
    end  
    
    concated_enum = [test_enum, test_enum2].lazy.flat_map{|en| en.lazy }
    

    然后在结果上调用next,可见副作用是懒惰发生的;

    [5] pry(main)> concated_enum.next
    hi
    => 1
    [6] pry(main)> concated_enum.next
    hi again
    => 2
    

    【讨论】:

    • 非常感谢flat_map 解决方案。尽管我们已经验证了 flat_map 的惰性,但我们并没有想到将其用作惰性附加! :)
    • 这个方案的缺点是concated_enum.size总是返回nil
    • @skalee 我认为这是惰性枚举的一个缺点;在您遍历它们之前,您永远无法知道它们有多大。它们的大小可能取决于您需要多长时间进行迭代。它们甚至可以是无限的。
    • enums.lazy.flat_map(&:lazy) 有点干净。还可以添加对其工作原理的解释。
    • 最近,从 Ruby 2.6 开始,您可以使用 Enumerable#chain/Enumerator::Chain。请参阅下面的答案。
    【解决方案2】:

    这里有一些 code I wrote for fun awhile back 引入了惰性枚举:

    def cat(*args)
      args = args.to_enum
    
      Enumerator.new do |yielder|
        enum = args.next.lazy
    
        loop do
          begin
            yielder << enum.next
          rescue StopIteration
            enum = args.next.lazy
          end
        end
      end
    end
    

    你会这样使用它:

    enum1 = [1,2,3]
    enum2 = [4,5,6]
    enum3 = cat(enum1, enum2)
    
    enum3.each do |n|
      puts n
    end
    # => 1
    #    2
    #    3
    #    4
    #    5
    #    6
    

    ...或者只是:

    cat([1,2,3],[4,5,6]).each {|n| puts n }
    

    【讨论】:

      【解决方案3】:

      从 Ruby 2.6 开始,您可以使用Enumerable#chain/Enumerator::Chain

      a = [1, 2, 3].lazy
      b = [4, 5, 6].lazy
      
      a.chain(b).to_a
      # => [1, 2, 3, 4, 5, 6]
      
      Enumerator::Chain.new(a, b).to_a
      # => [1, 2, 3, 4, 5, 6]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-04
        • 2020-05-17
        • 1970-01-01
        • 2013-12-03
        • 1970-01-01
        • 2010-09-11
        • 2012-07-17
        相关资源
        最近更新 更多