当前位置: 首页 > 知识库问答 >
问题:

实际上在不创建GOTO的情况下捕获异常

都阳
2023-03-14

查看我的Raku代码,我意识到我几乎从不使用CATCH块来实际捕获/处理错误。相反,我用try块处理错误,并测试未定义的值;我唯一使用的CATCH块就是以不同的方式记录错误。我似乎并不是唯一一个有这种习惯的人——看看Raku文档中的CATCH块,除了打印消息之外,几乎没有一个能处理任何意义上的错误。(Rakudo中的大多数CATCH块也是如此。)。

然而,我想更好地理解如何使用CATCH块。让我介绍几个示例函数,所有这些函数都基于以下基本思想:

sub might-die($n) { $n %% 2 ?? 'lives' !! die 418 }

现在,正如我所说的,我通常会使用这个函数

say try { might-die(3) } // 'default';

但我希望在这里避免这种情况,并在函数中使用CATCH块。我的第一本能是写作

sub might-die1($n) {
    $n %% 2 ?? 'lives' !! die 418
    CATCH { default { 'default' }}
}

但这不仅不起作用,而且(非常有益!)甚至没有编译。显然,CATCH块没有从控制流中删除(正如我所想的那样)。因此,该块,而不是三元表达式,是函数中的最后一条语句。好吧,很公平。这个怎么样:

    sub might-die2($n) {
ln1:    CATCH { default { 'default' }}
ln2:    $n %% 2 ?? 'lives' !! die 418
    }

(那些行号是标签。是的,它是有效的Raku,是的,它们在这里是无用的。但是SO没有给出行号,我想要一些。)

这至少可以编译,但它不符合我的意思。

say might-die2(3);  # OUTPUT: «Nil»

对于DWIM,我可以将此更改为

    sub might-die3($n) {
ln1:    CATCH { default { return 'default' }}
ln2:    $n %% 2 ?? 'lives' !! die 418
    }
say might-die3(3);  # OUTPUT: «'default'»

这两个函数揭示的是,CATCH块的结果并不像我跳出来的那样被插入到异常发生的控制流中。相反,异常导致控制流跳转到封闭范围的CATCH块。这就像我们写的一样(在一个交替的宇宙中,Raku有一个GOTO操作符[编辑:或者可能不是宇宙的交替,因为我们显然有一个NYIGOTO方法。每天学习新的东西…]

    sub might-die4($n) {
ln0:    GOTO ln2;
ln1:    return 'default';
ln2:    $n %% 2 ?? 'lives' !! GOTO ln1;
    }

我意识到一些异常的批评者说它们可以简化为GOTO语句,但这似乎有点牵强。

我可以(大部分时间)避免用模拟GOTO。简历方法,但是我不能按照我想的方式去做。具体来说,我不会写:

    sub might-die5($n) {
ln1:    CATCH { default { .resume('default') }}
ln2:    $n %% 2 ?? 'lives' !! die 418
    }

因为。简历不接受参数。我可以写

    sub might-die6($n) {
ln1:    CATCH { default { .resume }}
ln2:    $n %% 2 ?? 'lives' !! do { die 418; 'default' }
    }
say might-die6 3;  # OUTPUT: «'default'»

至少在这个特定的例子中,这是有效的。但我忍不住觉得这更像是一种黑客行为,而不是一个实际的解决方案,而且它不会很好地推广。事实上,我忍不住觉得我在Raku错误处理背后缺少了一些更深入的见解,这些见解将使所有这些更好地结合在一起。(可能是因为我花了太多时间用处理错误的语言编程,没有例外吗?)我希望能深入了解如何用惯用的Raku编写上述代码。上述方法之一基本正确吗?有没有其他方法我没有考虑过?在这一切中,我对错误处理有没有更深入的了解?

共有1个答案

强承望
2023-03-14

(在我的问题中)有一种方法基本正确吗?

是的。在一般情况下,使用像tryif这样的特性,而不是CATCH

有没有其他方法我没有考虑过?

这里有一个全新的:catch。几周前我发明了它的第一个版本,现在你的问题促使我重新思考它。我对现在的情况很满意;我很感谢读者对此事的反馈。

在这一切中,我对错误处理有没有更深入的了解?

我将在这个答案的最后讨论我的一些想法。

但是现在让我们按照你写的顺序来检查你的观点。

我几乎从不使用CATCH块来实际捕获/处理错误。

我也是。

相反,我用try块和测试未定义的值来处理错误

更像是这样。

我唯一使用的CATCH块就是以不同的方式记录错误。

正当一个位置合理的捕手。这是一个我认为CATCH非常适合的用例。

查看Raku文档中的CATCH块,除了打印消息之外,几乎没有一个块能够处理任何意义上的错误。

如果文件在以下方面有误导性:

>

替代方案;和/或

什么是惯用语(对于try更合适的代码,imo没有使用CATCH(现在我的新CATCH函数也是如此?)。

那就太不幸了。

(Rakudo中的大多数CATCH块也是如此。)。

我猜这些会被明智地放置。在调用栈用完之前放置一个,以指定默认的异常处理(作为一个警告加上。恢复,或一个die或类似的),对我来说似乎是合理的。他们都是这样的吗?

sub might-die1($n) {
    $n %% 2 ?? 'lives' !! die 418
    CATCH { default { 'default' }}
}

这不仅不起作用,而且(非常有用!)甚至无法编译。

.oO(这是因为您在第一个语句末尾忘记了分号)

(我本以为...CATCH块[会]从控制流中删除)

加入俱乐部。其他人在文件bug中表达了相关的观点,Q和A也是如此。我过去认为现在的情况和你所说的一样是错误的。我想我现在很容易被争论的任何一方说服——但jnthn的观点对我来说是决定性的。

引用文件:

移相器块只是包含它的闭包的一个特征,并在适当的时刻自动调用。

这表明相位器不是一个语句,至少在一般意义上不是,并且可以推测,它将从普通控制流中删除。

但回到医生那里:

相位器[可能]有一个运行时值,如果[在]周围表达式中进行计算,它们只需保存其结果以在表达式中使用。。。当表达式的其余部分求值时。

这表明它们可以有一个普通控制流意义上的值。

如果相位器不返回值,则不将其从普通控制流中移除,而是评估为Nil,其原理可能如下:

>

  • INIT这样的相位器返回值。编译器可以坚持将结果分配给变量,然后显式返回该变量。但那将是非常不拉库式的。

    Raku的理念是,一般来说,开发人员会告诉编译器做什么或不做什么,而不是相反。相位器是一种语句。如果将语句放在末尾,则希望它是其封闭块返回的值。(即使是Nil

    不过,总的来说,我在以下几点上支持你:

    >

    编译器IWBNI似乎至少警告过,如果它看到一个非值返回相位器被用作包含其他值返回语句的块的最后一条语句。

    好吧,很公平。这个怎么样:

        sub might-die2($n) {
    ln1:    CATCH { default { 'default' }}
    ln2:    $n %% 2 ?? 'lives' !! die 418
        }
    
        say might-die2(3);  # OUTPUT: «Nil»
    

    如上所述,许多相位器,包括异常处理相位器,都是不返回值的语句。

    我认为人们可以合理地预期:

    >

  • CATCH相位器将返回一个值。但他们没有。我依稀记得jnthn已经在SO上解释了为什么;我将把寻找它作为读者的练习。或者,相反地:

    编译器会警告,没有返回值的移相器被放置在可能预期返回值的位置。

    就好像我们写了...一个GOTO操作符

    Raku(do)不仅仅是做一个非结构化的跳跃。

    (否则,.resume将不起作用。)

    这似乎有点牵扯太远了

    我同意,你有点太过分了。: P

    可恢复的例外情况当然不是我在Raku所追求的。我认为我还没有在“用户空间”代码中使用它们。

    (从jnthn的回答到我希望何时恢复Raku异常?)

    .resume不接受html" target="_blank">参数

    正当它只是在导致抛出异常的语句之后恢复语句的执行.resume不会更改失败语句的结果。

    即使一个CATCH块试图进行干预,它也不能通过设置其赋值引发异常的变量的值,然后进行干预。RakuCATCH块能够改变词法范围内的变量?。

    (我尝试了几种与CATCH相关的方法,然后得出结论,使用try是我在一开始链接的CATCH函数体的方法。如果您还没有查看CATCH代码,我建议您这样做。)

    出于几个原因,他们有点令人担忧。其一是似乎有意限制其预期能力和适用性。另一个是虫子。例如,考虑:

    >

    Rakudo问题:调用时缺少do的返回值。resumeCATCH是块中的最后一条语句

    Rakudo问题:返回-从块中取出并离开移相器(“标识”)

    在这一切中,我对错误处理有没有更深入的了解?

    可能我想你已经很清楚了,但是:

    >

  • KISS#1您已经处理了其他PLs中没有异常的错误。它起作用了。你在拉库做到了。它工作。仅在需要或想要使用异常时使用它们。对于大多数代码,你不会。

    KISS#2忽略一些本机类型用例,几乎所有结果都可以表示为有效或无效,而不会导致半谓词问题,使用以下Raku真值的简单组合,提供人机工程学方法来区分非错误值和错误:

    >

  • 条件:ifwhiletry/,等等

    谓词:.so.defined.DEFINITE,等等

    值/类型:NilFailures,零长度复合数据结构,:Dvs:U类型约束,等等

    坚持错误例外,我认为有几点值得考虑:

    >

  • Raku错误异常的一个用例是覆盖与Haskell中的异常相同的基础。在这些情况下,将它们作为值处理不是正确的解决方案(或者,在Raku中,可能不是)。

    其他请支持例外情况。Raku的超级大国之一是能够与所有其他PLs进行互操作。因此,它支持异常,除非是为了实现正确的互操作。

    Raku包括故障的概念,即延迟异常。我们的想法是,你可以两全其美。谨慎处理,故障只是一个错误值。不小心处理,它像一个正常的例外。

    更一般地说,Raku的所有功能设计为协同工作,以提供方便但高质量的错误处理,支持以下所有编码方案:

    >

    鲁棒性控制。逐渐缩小或扩大错误处理。

    多样化的选择。什么错误应该发出信号?什么时候?用哪个代码?如果使用代码是为了表明生成代码应该更加严格呢?还是更放松?如果是相反的情况呢--生成代码想要发出使用代码应该更加小心或者可以放松的信号?如果产生和使用代码有冲突的哲学,该怎么办?如果生成的代码不能被修改(例如,它是一个库,或者用另一种语言编写),该怎么办?

    语言/代码库之间的互操作。唯一有效的方法是Raku提供高水平的控制和多样化的选择。

    在这些场景之间进行方便的重构。

    所有这些因素以及更多因素构成了Raku错误处理方法的基础。

  •  类似资料:
    • 我有一个实现以下接口的基类,上面声明的方法抛出基本异常类型。 有许多具体的类扩展了基类,我不想在所有这些类中添加块。有没有一种方法可以处理而不添加? 接口 基层 混凝土类示例(共30个)

    • 问题内容: 如果我有与一起使用的代码,如何在不实际创建与localhost的网络连接的情况下为其编写测试? 我在网上没有看到任何解决方案。人们似乎忽略了它(没有测试),编写了不能并行运行的测试(即使用实际的网络连接,使用了端口),或者使用了io.Pipe。 然而,限定,; 而io.Pipe没有。net.Pipe 也 没有,尽管从表面上声称实现了该接口,但它只是通过以下方式实现: (请参阅:http

    • 我即将对我的项目进行一个重大的技术更改,从ANT转移到Gradle。这个项目从来没有发布过--它仍在开发中。我想标记最后的旧技术版本,以便可以找到参考或逆转。但我不想暗示有任何一种释放。 然后 我之前向GitHub提交了一个支持请求,但当时他们有一些支持积压,所以我的请求被关闭了。

    • 我的javaFX项目中有一辆汽车,当空间被挤压时,汽车(节点)的位置应该改变(汽车应该平滑地跳跃)。所以我使用了一个事件处理程序来调用一个名为< code>moveUp()的方法 这创建了一个新的线程,其中汽车的速度以75毫秒的间隔改变10次。 如果我不使用其他线程,GUI线程将被冻结,如果我不使用Thread.sleep(),汽车将突然跳跃(不顺利)。这段代码做得很好。但是我了解到 并不能保证线

    • 我为我的系统创建了一些报告,该报告由许多表组成。为此,我创建了一个带有@Entity注释的域类,并实现了一个JpaRepository存储库,我将本机查询与@query一起使用,如下所示。 我的问题是,对于每个域类,hibernate都在创建一个表,如何停止它? 我的域类: 我的存储库:

    • 我在这里陷入了僵局。 我在ArrayList中存储了值。每个值都是一个字符串,它由几个其他字符串组成,用分隔符(##)分隔。 例如:ArrayList lst有以下4个值。 null string[]arry=erow.split(“##”); 在这个for循环中,我使用另一个for循环for arry.length()来获取每个字符串(比如aaa),并使用Apache POI将其写入excel上