无法理解Triagle数枚举器yielder yield

时间:2018-12-04 10:01:05

标签: ruby

无法理解此代码的工作原理。

triangular_numbers = Enumerator.new do |yielder|
 number = 0
 count = 1
 loop do
    number += count
    count += 1
    yielder.yield number  
  end
 end
5.times { print triangular_numbers.next, " " }

不能理解yielder在该块中的工作原理。 yieldnumber变量的作用。循环如何运行5次。以及triangular_number.next的首次使用方式。

4 个答案:

答案 0 :(得分:4)

Enumerator::new接受一个阻止。该块在运行时会收到一个Enumerator::Yielder,它具有方法#yield

调用Enumerator#next时,将执行该块,直到第一个Enumerator::Yielder#yield。执行在此处暂停;给yield的值就是next返回的值。当您在同一next上再次调用Enumerator时,将恢复执行,并继续执行直到再次遇到yield


因此,在您的情况下,5.times执行其块,打算将其重复五次。 triangular_numbers.next被调用;这开始执行上面的块。将numbercount设置为其值,并开始无限循环。将number设置为1,将count设置为2,然后我们找到yielder.yield。这将暂停该块的执行,并将控件返回到next循环内调用5.times的位置。 next返回1,因为yielder.yield收到了number1)。

第二次通过5.times循环,我们要打印下一个数字。这将停止主执行行,并从yielder.yield之后开始继续枚举器块。无限循环继续; number3count3yielder.yield暂停枚举器并恢复主代码。 next得到3,它被打印出来。

5.times循环中的第三,第四和第五次完全相同。

五次迭代后,5.times循环结束,执行越过它。枚举器已暂停,如果您再次调用next(因为它具有无限循环),则准备按顺序给出下一个数字,但是您从不执行,则程序退出。

答案 1 :(得分:4)

enumerator基本上是可以调用next并取回东西的东西。 yielder是一种机制,在调用next时它会返回一些信息。执行将在yield停止,直到下一次调用next

牵强的类比

您可以将enumerator视为票务机,就像您在政府办公室排队等候时一样。当您按下按钮(next)时,它会给您门票。机器内部有一个滑槽,可以出票。但是售票机并不是一直在打印票。它等待按钮被按下,然后再打印下一张票并将其穿过滑道。

在这种情况下,类似的代码将是:

ticket_machine = Enumerator.new do |chute|
 ticket = 0
 loop do
    #print_ticket
    chute.yield ticket #waits here until you hit the button
    ticket += 1
  end
 end
5.times { print ticket_machine.next, " " } # gets 5 tickets

您的代码示例基本上是同一回事,但是它不是发行票证,而是发行三角数。 滑道是数字通过的yielder

这不是使用枚举器check the docs for more.

的唯一方法

答案 2 :(得分:0)

我将尝试一点一点地解释它的作用,因此您可以尝试将其包裹住。

Enumerator.new do |yielder|
end

因此,您实例化了一个将在名为yielder的变量上工作的枚举器。

在其作用域内,您设置了一些局部变量(将在重用对象时保留):

 number = 0
 count = 1

然后设置一个循环,将number递增count,将count递增1,然后调用传递number的参数的yield作为一个论点。

loop do
  number += count
  count += 1
  yielder.yield number  
end

5.times重复传递给它的块5次。区块

-> { print triangular_numbers.next, " " }

调用print并使用n args并将这些部分连接起来以形成字符串,但不附加换行符。

第一个参数是我们的枚举器下一次交互(triangular_numbers.next),它将计算当前数并在隐式创建的Enumerator::Yielder上调用收益,将控件处理回调用方Fiber以及传递给它的所有参数。

(所有枚举器都在MRI上实现为“光纤”)

因此,yielder.yield调用类似于Fiber.yield调用,并允许5.times循环运行并返回number 1。

答案 3 :(得分:0)

我在提供的已经很清楚的解释中添加了一段代码:

my_enum = Enumerator.new do |whatever_name_for_the_yielder|
  n = 0
  loop do
    whatever_name_for_the_yielder.yield "Return this: #{n}"
    n += 1
  end
end

puts my_enum.next #=> Return this: 0
puts my_enum.next #=> Return this: 1
puts my_enum.next #=> Return this: 2

当您结束迭代时,它会因错误而停止:

my_enum2 = Enumerator.new do |whatever_name_for_the_yielder|
  2.times do |n|
    whatever_name_for_the_yielder.yield "Return this: #{n}"
  end
  puts "Outside the loop"
end

puts my_enum2.next #=> Return this: 0
puts my_enum2.next #=> Return this: 1
puts my_enum2.next #=> Outside the loop
#=> ERROR: .....in `next': iteration reached an end (StopIteration)