【问题标题】:Use of exceptions in Ada在 Ada 中使用异常
【发布时间】:2020-07-22 15:30:07
【问题描述】:

我正在寻找风格指导。在 Python 中,异常被用作“正常”操作:

try:
    z = x/y
except ZeroDivisionError:
    z = 73.0    # set z if y is zero

我们不是检查 y 是否接近于零,而是进行除法并捕获异常。

John Barnes 在 Ada 中的“Programming in Ada 2012”中说明了这种方法:

begin
    Tomorrow := Day'Succ(Today);
exception
    when Constraint_Error =>
        Tomorrow := Day'First;

但是,这本书接着说“这是一个非常糟糕的例子。例外应该用于很少发生的情况......”。

我是 Ada 的新手,像我们在 Python 中那样使用异常来避免 if 语句是一种好的 Ada 编程风格吗?还是真的只是在 Ada 中将异常用于讨厌的事情?

【问题讨论】:

  • 我离python专家还很远,对Ada一无所知,但据我所知,python是特别容易上手的和快速的try-catch 结构。在我知道的任何其他编程语言中,try-catch严重影响性能,因此您应该只在无法通过一些解析/检查处理它时才使用它。也许这就是这本书所说的。
  • 同意@AnderBiguri。 try-catch 的广泛使用是 Python 设计的一部分,被认为是一种 pythonic 风格,这可能只对 Python 有意义,但是嘿:我们可能错了。
  • Ada 中的异常成本取决于实现;更多here.
  • 风格可读性的角度来看,你的sn-p非常好!

标签: ada


【解决方案1】:

与 Python 的一个主要区别在于,在 Ada 中,异常不是数据类型。唯一可以与异常实例关联的数据是字符串:

raise My_Exception with "some string";

因此,当捕获异常时,我们只能知道引发了哪种异常,并获取一些可能对日志记录有用的字符串,但对于以编程方式更详细地分析出了什么问题却没有多大用处。

这也意味着我们没有办法链接异常,就像它经常做的那样,例如在 Java 中,一个异常通常被包装在另一个异常中以隐藏细节,同时仍然能够在堆栈跟踪中查看到底出了什么问题。

这样做的原因是该语言被设计为很少使用例外。与 Python 不同,Ada 旨在针对裸机,出于性能原因,您可能希望在其中禁用运行时检查。如果你这样做,你将不会得到 Constraint_Error 除以零,并且程序将具有未定义的行为。所以使用异常会对代码的可移植性产生负面影响。

一些编译器提供了额外的选项,比如 GNAT 中的No_Exception_Propagation 限制,它只允许在同一个子例程中捕获异常。

不过,异常是一种在不完全阻碍代码中正常程序流程的情况下传达故障的有用工具(如果您曾经编写过 Go 或 C,您就会明白我的意思)。但一般来说,Ada Style Guide advises that

应将异常用作抽象的一部分,以指示抽象无法防止或纠正的错误情况。因为抽象无法纠正这样的错误,所以必须向用户报告错误。

这意味着,如果您确实有可能在不使用异常的情况下纠正问题,那么您应该这样做,因为它应该是正常的、非异常的程序流的一部分。

【讨论】:

    【解决方案2】:

    Ada 异常处理语法不使用 try-catch 机制。因此,异常处理程序通常通过多行源代码与引发异常的代码分开。 Ada 异常有望用于真正异常的问题。 Ada 更可接受的样式如下所示。

      if Today < Day'Last then
         Tomorrow := Day'Succ(Today);
      else
         Tomorrow := Day'First;
      end if;
    

    此 Code Pattern 保留了源代码中非常接近的两个条件之间的关系。这两种情况都不是错误情况或异常情况。

    从历史上看,Ada 风格更倾向于可读性而不是写作的紧凑性,但是 Ada 2012 标准确实允许条件表达式,例如

    Tomorrow := (if Today < Day'Last then Day'Succ(Today) else Day'First);
    

    是否使用条件表达式可能是个人喜好问题。

    【讨论】:

    • 我想说begin-exception-end 在语法和语义上都非常接近try-catch。
    • 当用于代替条件语句或表达式时,它要求您为每个条件创建一个块。每个块都会创建一个新的范围并影响可见性规则。不必要的块不仅仅是增加视觉复杂性。它们是对未来维护错误的邀请。
    • 我没有反对这些含义。我只是不明白 try-catch 与 begin-exception-end 有什么不同。如果没有declare,新作用域无论如何都不包含任何符号。
    【解决方案3】:

    异常的存在是为了表示异常情况,确保它们得到响应(与可能被忽略的状态代码不同),并将它们的处理与非异常情况分开。虽然错误是异常情况,但并非所有异常情况都是错误。例如,在处理用户输入时,无效输入是异常的,但有时是预期的;使用异常来处理这个是合理的。我认为 Ada 将其所有预定义的异常命名为以 _Error 结尾的名称是错误的。

    有时有必要使用异常,即使最好避免它们。例如,如果一个文本文件以空行结尾,您想使用Ada.Text_IO 读取该文件,并且读取该空行很重要,您不能使用Ada.Text_IO.End_Of_File。您必须继续阅读并处理由此产生的End_Error 异常。

    在特殊情况和特殊情况之间划清界限取决于技能、经验以及在生成代码之前您做了多少思考。如果您需要在系统中大量循环遍历一组值(例如日期名称),编码人员将在整个代码中散布if 语句或带有异常处理程序的块语句,更好的编码人员将创建一个函数来执行此操作对于所涉及的每种类型,软件工程师都会发现重用的机会并找到或创建类似 PragmARC.Wrapping 的内容,因此只需编写

    Tomorrow := Wrap_Succ (Today);
    

    不用担心Wrap_Succ 是使用if 还是异常处理程序。

    【讨论】:

      猜你喜欢
      • 2018-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-11
      • 2011-08-26
      • 2013-01-05
      相关资源
      最近更新 更多