无法理解此代码的工作原理。
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
在该块中的工作原理。 yield
对number
变量的作用。循环如何运行5次。以及triangular_number.next
的首次使用方式。
答案 0 :(得分:4)
Enumerator::new
接受一个阻止。该块在运行时会收到一个Enumerator::Yielder
,它具有方法#yield
。
调用Enumerator#next
时,将执行该块,直到第一个Enumerator::Yielder#yield
。执行在此处暂停;给yield
的值就是next
返回的值。当您在同一next
上再次调用Enumerator
时,将恢复执行,并继续执行直到再次遇到yield
。
因此,在您的情况下,5.times
执行其块,打算将其重复五次。 triangular_numbers.next
被调用;这开始执行上面的块。将number
和count
设置为其值,并开始无限循环。将number
设置为1
,将count
设置为2
,然后我们找到yielder.yield
。这将暂停该块的执行,并将控件返回到next
循环内调用5.times
的位置。 next
返回1
,因为yielder.yield
收到了number
(1
)。
第二次通过5.times
循环,我们要打印下一个数字。这将停止主执行行,并从yielder.yield
之后开始继续枚举器块。无限循环继续; number
为3
,count
为3
,yielder.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)