mic*_*hau 2 ruby loops break proc
breakproc中的三个循环一直跳到puts 8?这很反直觉.puts 6?3.times do
puts "outer loop"
break_proc = proc { break }
puts 1
loop do
puts 2
loop do
puts 3
loop do
puts 4
break_proc.call
puts 5
end
puts 6
end
puts 7
end
puts 8
end
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)outer loop 1 2 3 4 8 outer loop 1 2 3 4 8 outer loop 1 2 3 4 8
您看到的行为是尝试将Proc对象视为传递给Kernel#eval的代码片段,或者认为Proc内部的顶层中断与break循环内的裸关键字相同.提供了对行为的解释,但真正的解决方案是避免做你正在做的事情.
为什么一个proc中的break会跳出三个循环,一直到8个?
发生这种情况是因为Proc对象包含对其创建的上下文的绑定,并且break 关键字正在退出迭代器块并返回其调用上下文.具体来说,您将在顶层循环中创建Proc:
3.times do
puts "outer loop"
break_proc = proc { break }
Run Code Online (Sandbox Code Playgroud)
人们可以原谅认为Ruby break只是在它被调用的地方退出一个循环,但它的行为比这更复杂,特别是当你试图做一些奇怪的事情,如在Proc内部的顶级突破.您的用例break甚至包含在Ruby编程语言中,其中包含:
[break]导致块返回其迭代器,迭代器返回调用它的方法.因为procs像块一样工作,我们希望break在proc中做同样的事情.但是,我们不能轻易地测试它.当我们用Proc.new创建一个proc时,Proc.new就是break将返回的迭代器.当我们可以调用proc对象时,迭代器已经返回.因此,在使用Proc.new [.]创建的proc中使用顶级break语句永远不会有意义.
- David Flanagan和Yukihiro Matsumoto.Ruby编程语言(Kindle Locations 8185-8192).奥莱利媒体.
当您创建深层嵌套循环,然后使用带有运行时绑定的对象使其复杂化时,结果并不总是您所期望的.你看到的行为不是一个错误,虽然在某些情况下它可能是一个错误.如果你想要实现语义的原因而不是对你所看到的行为的解释,你必须向语言设计者询问它为什么会这样做.
有没有办法让它突破最里面的循环,也就是说,放入6?
是的,但不是break在Proc内部.用实际的内联break语句替换Proc#调用可以达到预期效果,并且是"最简单的可能工作",但如果要调整嵌套级别,也可以使用throw和catch.例如:
3.times do
puts "outer loop"
break_proc = proc { throw :up }
puts 1
loop do
puts 2
loop do
puts 3
catch :up do
loop do
puts 4
break_proc.call
puts 5
end
end
puts 6
end
puts 7
end
puts 8
end
Run Code Online (Sandbox Code Playgroud)
这将产生:
Run Code Online (Sandbox Code Playgroud)outer loop 1 2 3 4 6 3 4 6 3 4 6
并在你第三个循环内无休止地循环puts 3.
所以,这将做你所要求的,但可能会或可能不会做你想要的.如果它有帮助,太棒了!如果没有,如果您想要找到更优雅的数据结构或将您的任务分解为一组协作对象,您可能想要提出一些单独的问题,其中包含一些真实的数据和行为.
| 归档时间: |
|
| 查看次数: |
262 次 |
| 最近记录: |