2017-08-24 71 views
2

我觉得有点奇怪,因为有一些问题有点像这样,但是不够接近或者没有答案。ruby​​:在异常后继续进行同样的循环迭代

[编辑:我改写的问题,以使其更清晰]

我有一个循环,做了一些事情,并用它玩,看看各种选项将努力使事情更易读,清洁器。最后,我搞砸了一些东西,它被抛出并且异常,这是在救援时遇到的。到现在为止还挺好。然而,我不能找到任何方法让ruby在循环的同一次迭代中继续使用以下语句 - 异常总是会产生以下语句,并转移到下一次迭代 - 默认行为。重做或重试将是毫无意义的,并导致无限循环,再次发生同样的错误和异常,等等,

我有什么办法强制ruby以某种方式处理错误,然后只是继续它离开的地方?

这里是一个样本,从我不知道在哪里偷来,并适应。

3.times do |i| 
    begin 
    # first bunch of stuff to do 
    # second bunch of stuff to do 
    # third bunch of stuff to do 
    # fourth bunch of stuff to do 
    rescue => e 
    p e 
    end 
end 

基本上,在这个例子中,我想要的东西串的所有四个对移动到循环的下一次迭代中,即使其中一个应该引起错误之前执行。我不是要处理这个错误,让我看到异常结果等等,但是有效地忽略它并继续做其他所有事情。

任何想法?这可能是我以前从未见过的真正明显的事情。

+0

无论是否发生异常,是否应该执行'puts'? – Stefan

+0

是的。这模拟了我正在搞乱的主程序中的字符串操作等等。 – MrBungleBear

+0

如果您使用故意的流量控制异常,这可能是一个错误。为什么不只是一个'if'? – tadman

回答

2

你定义一个与rescue子句begin-end块。如果任何异常是由块引发的并且有一个匹配的救援条款,则该块将停止执行,并执行救援条款。如果没有匹配的救援块,这个错误就会冒泡(并且希望能够被另一个块处理,否则它将被处理并且你的脚本将停止!)如果存在子句,那么即使存在例外。

那么,这是否离开我们?如果要抵御的各个步骤失败,继续不分,每一步都需要自己的块:

3.times do |i| 
    begin 
    first_thing 
    rescue => e 
    puts "The first thing blew up! #{e.inspect}" 
    puts "I'll carry on anyway ¯\\_(ツ)_/¯" 
    end 

    begin 
    second_thing 
    third_thing 
    rescue => e 
    puts "Either the second or third thing blew up... #{e.inspect}" 
    puts "Straight on to the fourth thing!" 
    end 

    begin 
    fourth_thing 
    rescue => e 
    puts "Fourth thing blew up! #{e.inspect}" 
    end 
end 

这有点不寻常的一个块这样应该对执行进行,如果出现错误 - 这通常是让下一个步骤之一出错的好方法!您可能需要确保您在每个时间点仍然具有数据完整性,并且后面的步骤不依赖于在先前步骤中可能未发生的事情。

+0

谢谢所有 - 我想我有我的答案。没有什么内置的,真的,做错误处理然后继续。如果我想要做到这一点,单独的区块就可以走了。这有点笨重,但你很少能够获得生活中的一切!这里的基础是从一个文件中读取一行,在检查网络已经存在并且可以打开并且我们实际上已经打开一行,然后对它执行大量独立的事情,所以我很舒服直到这种错误和情况,这让我在浪费时间在非工作解决方案上花了一整天的时间后走上了这条路。 :) – MrBungleBear

2

A begin块(包括来自说明def的隐含块)以第一个异常结束。无论成功/失败,如果你想单独做某件事,那就把它放在块之外。

3.times do |i| 
    begin 
    raise "Raised from iteration #{i}" 
    rescue => e 
    p e 
    end 
    puts "I'm after the exception" 
end 

如果你想即使经过returnbreak做一些事情,等于是使用ensure块。

5.times do |i| 
    begin 
    break if i == 3 
    raise "Raised from iteration #{i}" 
    rescue => e 
    p e 
    ensure 
    puts "I always run #{i}" 
    end 
end 

,输出:

#<RuntimeError: Raised from iteration 0> 
I always run 0 
#<RuntimeError: Raised from iteration 1> 
I always run 1 
#<RuntimeError: Raised from iteration 2> 
I always run 2 
I always run 3 

如果你真的想忽略和可能引发许多语句继续,他们每个人都需要一个单独的rescue块。你可以用另一个包装方法来做到这一点。然而,要非常小心,被忽视的例外并不重要,而是继续是安全的。

设计异常,意味着放弃以下操作,并尽量避免使不一致和未知的程序状态。

def suppress 
    yield 
rescue => e 
    puts "Supressing #{e}" 
end 
5.times do |i| 
    suppress { raise "I throw sometimes #{i}" if i <= 3 } 
    suppress { raise "I throw sometimes too #{i}" if i > 2 } 
    puts "After possible exceptions #{i}" 
end 

输出:

Supressing I throw sometimes 0 
After possible exceptions 0 
Supressing I throw sometimes 1 
After possible exceptions 1 
Supressing I throw sometimes 2 
After possible exceptions 2 
Supressing I throw sometimes 3 
Supressing I throw sometimes too 3 
After possible exceptions 3 
Supressing I throw sometimes too 4 
After possible exceptions 4 
+0

感谢Fire Lancer - 但是,如果您知道某段代码可能会通过并发生异常,那么该方法就有效。如果你不知道,或者不知道哪一块,你想拯救环绕所有的代码,所以你不一定这样做。想象一下,提高实际上是一个负载的东西,可能在某种程度上,给定一些条件,抛出一个异常。 – MrBungleBear

+0

一般来说,它不安全,在发生异常之后继续“下一个语句”,并且会很困难,如果你真的想,你需要以某种方式明确地包装它们,或者单独展开开始块或者一些包装器方法 –

+0

你不想写YOLO代码,通过异常来推土机,并继续前进。 – tadman