代码之家  ›  专栏  ›  技术社区  ›  David

帮助理解Ruby中的yield和枚举器

  •  4
  • David  · 技术社区  · 16 年前

    如果有人能帮助我理解在枚举器中使用yielder与在枚举器中调用yield之间的区别,我将不胜感激。

    “根深蒂固的红宝石主义者”表明,一个人不是“屈服于街区”,而是没有准确解释到底发生了什么。

    谢谢

    3 回复  |  直到 11 年前
        1
  •  2
  •   Jörg W Mittag    16 年前

    这个 Enumerator::Yielder#yield 方法与 Enumerator::Yielder::<< 方法是 完全一样 . 实际上,它们是别名。

    所以,你使用的这两个选项中的哪一个是100%的个人偏好,就像 Enumerable#collect Enumerable#map Enumerable#inject Enumerable#reduce .

        2
  •  5
  •   7stud    11 年前

    如果你首先了解收益率是如何工作的,这可能会有所帮助。下面是一个例子:

    def do_stuff
      if block_given?
        yield 5
      else
        5
      end
    end
    
    result = do_stuff {|x| x * 3 }
    puts result
    
    --output:--
    15
    

    在do-tuff方法调用中:

    do_stuff {|x| x * 3 }
    

    ..块就像一个函数,它被传递给do-tuff方法。在do-tuff中,yield调用函数并传递指定的参数——在本例中是5。

    需要注意的一些重要事项:

    1. 产量叫做 内部方法

    2. 调用方法时,可以将块传递给该方法

    3. yield用于调用块。

    好吧,现在让我们看看你的评论问题:

    是真的吗?

    e = Enumerator.new do |y| 
      y << 1 
      y << 2 
      y << 3 
    end 
    

    e = Enumerator.new do   #I think you forgot to write .new here
        yield 1 
        yield 2 
        yield 3 
    end
    

    在第二个例子中,任何地方都没有方法定义——所以不能调用yield。错误!因此,这两个例子并不相同。

    但是,您可以这样做:

    def do_stuff
      e = Enumerator.new do 
          yield 1 
          yield 2 
          yield 3 
      end 
    end
    
    my_enum = do_stuff {|x| puts x*3}
    my_enum.next
    
    --output:--
    3
    6
    9
    1.rb:12:in `next': iteration reached an end (StopIteration)
        from 1.rb:12:in `<main>'
    

    但这是一个有趣的枚举器,因为它不产生任何值——它只执行一些代码(碰巧打印了一些输出),然后结束。这个枚举器几乎等同于:

    def do_stuff
      e = Enumerator.new do 
      end 
    end
    
    my_enum = do_stuff
    my_enum.next
    
    --output:--
    1.rb:7:in `next': iteration reached an end (StopIteration)
        from 1.rb:7:in `<main>'
    

    当枚举器无法生成值时,它会引发StopIteration异常。所以在这两种情况下,枚举器都不能产生值。

    但我还是不清楚“一长老”在做什么。它看起来 就像它收集所有的计算值那样 稍后使用枚举器时将其反流。如果那是 如果是这样,那么它似乎只适用于“小” 序列……您不想让一个存储了50个 百万件物品。

    不可以。实际上,您可以创建一个枚举器来产生无限多的值。下面是一个例子:

    e = Enumerator.new do |y|
      val = 1
    
      while true
        y << val
        val += 1
      end
    
    end
    
    puts e.next
    puts e.next
    puts e.next
    
    --output:--
    1
    2
    3
    

    添加一些调试消息应该证明是有洞察力的:

    e = Enumerator.new do |y|
      val = 1
    
      while true
        puts "in while loop"
        y << val
        val += 1
      end
    
    end
    
    puts e.next
    
    --output:--
    in while loop
    1
    

    请注意,该消息只打印一次。所以发生了一些不明显的事情:

    e = Enumerator.new do |y|
      val = 1
    
      while true
        puts "in while loop"
        y << val
        puts "just executed y << val"
        val += 1
      end
    
    end
    
    puts e.next
    
    --output:--
    in while loop
    1
    

    因为“刚执行Y<<val”消息没有显示在输出中,这意味着执行必须在线路上停止。 y << val . 因此,枚举器没有连续旋转while循环并将所有值插入y中——即使语法与将值推入数组完全相同: arr << val .

    什么 V.Y.L. 真正的意思是:当调用e.next()时产生这个值,然后在下一行继续执行。如果在前面的示例旁边添加另一个e.

    just executed y << val
    in while loop
    2
    

    发生的是,当 V.Y.L. 在代码中遇到。然后调用e.next在右侧生成值,然后在下一行继续执行。

    如果Ruby对Yield语句的语法是这样的,可能会更有意义:

    y >> val
    

    我们可以将其解释为:在这里停止执行,然后当e.next被称为productval时。

    David Black建议不要使用 y.yield val 语法,相当于 V.Y.L. 以免读者认为它的工作原理与收益率声明类似。 屈服瓦尔 应该解释为:“在这里停止执行,当next被称为product val时,在下一行继续执行。我个人认为 V.Y.L. 脱颖而出 屈服瓦尔 因此,更容易在代码中发现并容易识别执行停止的位置。

        3
  •  3
  •   Telemachus MrJames    16 年前

    除非我漏掉了什么 yield 根本不起作用。试试看:

    e = Enumerator.new do |y|
      y << 1
      y << 2
      y << 3
    end
    
    f = Enumerator.new do
      yield 1
      yield 2
      yield 3
    end
    
    e.each { |x| puts x }
    f.each { |x| puts x }
    

    这就产生了:

    telemachus ~ $ ruby yield.rb 
    1
    2
    3
    yield.rb:13:in `block in <main>': no block given (yield) (LocalJumpError)
            from yield.rb:19:in `each'
            from yield.rb:19:in `each'
            from yield.rb:19:in `<main>
    

    当他说(第304页)“你 不要 “这样做,”他不是说“这不是最好的方法。”他是说,“那不管用。”

    编辑:但是,您可以通过以下方式显式调用yield:

    e = Enumerator.new do |y|
      y.yield 1
      y.yield 2
      y.yield 3
    end
    

    如果你觉得 产量 << ,然后这样做。

    第二次编辑:看看大卫的原始帖子和乔格的最新答案,我认为最初关于这个问题有一个困惑。乔格认为大卫在问 Enumerator::Yielder#yield Enumerator::Yielder::<< 但是大卫不确定 脚踏实地的红人 意思是当它说“不要写” yield 1 我的回答适用于 脚踏实地的红人 . (当我今天回顾这条线索时,考虑到其他更新,我的答案看起来很奇怪。)