Julia 异常处理

Julia 异常处理(try,catch,finally),由于输入的不确定性、运行环境的变更、交互的复杂性或设计的缺陷,程序总会存在一些漏洞(Bugs)。可以说没有不存在Bug的程序。

很多情况下Bug在发布前是难以发现的,都是在实际使用中不断地出现再不断地修复,但在这不断优化改善的过程中又有可能引入新的漏洞或缺陷。这是软件工程常见的客观情况。为此,在必要的时候,对一些代码采取预防措施能够适当地减少异常引发的程序崩溃。所以,异常处理机制也是很多语言的标配。

异常触发

由于程序设计时,每段代码的正确执行都需要特定的前置条件,而通常这些条件是可知的。一旦条件不满足,会导致后续主要逻辑的不正常或无法运行,为此可以对这些可预知的“错误”进行提前设定处置逻辑,这些分支逻辑即是异常处理逻辑。

另外,既然是可知的错误,就可以预先对某些常见的错误进行定义,以便于后续的跟踪与处置。Julia中内置定义了很多错误类型,均继承自抽象类型Exception

对于这些预定义的错误,可在需要的时候通过throw()函数显式地抛出,以上报该处代码发生的问题,并方便后续的调试与处置。

例如有一个函数只能处理非负数,却收到了负数参数,如下所示:
Julia 异常处理

可见一旦运行条件不符合要求,DomainError的实例便会被抛出,同时会传递关于异常的描述信息。

提示
在异常信息之后,同时会有Stacktrace字段给出错误发生的具体函数原型及其定义的位置。因为该字段主要提示错误发生的位置,所以每次原型都会有所差异。

除了预定义的错误类型,还有一个通用的异常类型ErrorException,能够在上报时以简短的信息描述错误的情况。若是希望自定义异常类型,则可声明Exception新的子类型,此后便可通过函数throw()在需要的时候上报新声明的错误类型,以便进行恰当的处置。

若无须针对错误的类型进行后续处理,而仅需对错误进行提示,则可直接通过@error宏上报。例如:

julia> fussy_sqrt(x) = x >= 0 ? sqrt(x) : @error "negative x not allowed"
fussy_sqrt (generic function with 1 method)

julia> fussy_sqrt(2)
1.4142135623730951

julia> fussy_sqrt(-1)
┌ Error: negative x not allowed          # 在REPL中Error会以红色显示
└ @ Main REPL[36]:1

这其实并非属于异常类型机制中的内容,而是开发中最为常用的日志工具,也是发现问题最为简单有效的手段。

@error类似的还有@info, @warn@debug,分属于不同的日志级别,依次为常规信息、警告信息及调试信息。当然,它们打印的信息前缀是有所区别的,例如:
Julia 异常处理

关于日志方面,Julia有一个Logging包,提供了更为全面的功能,开发者可以根据需要安装使用。

异常捕捉

在异常发生时,throw()抛出错误后,需要对其进行捕捉才能在恰当的位置有针对性地进行处置。在Julia提供的捕捉机制中,将可疑语句放在try和end组成的异常处理结构中,如下所示:

        try
          # 正常语句块
        catch异常类型变量
          # 异常处理语句块
        finally
          # 收尾清理语句块
        end

其中,try和end是必需的,两者之间的语句块便是预期正常运行的业务代码,可视为复合表达式;catch是异常捕捉语句,其后一般会跟着一个“异常类型变量”,用于提取try中发生之的异常类型,可针对异常类型变量的不同情况进行异常处置;而finally中的语句块用于清理收尾,该部分的代码无论是否发生异常,都会执行。

在异常捕捉结构中,除了tryend必需外,catchfinally两者也必须至少提供一个。下面给出一个的例子:

julia> try
          log(-10)
        catch ex
          tuprintln("exception type: ", ex)
        finally
          println("here is finally")
        end
exception  type:  DomainError(:log,  "-10.0  will  only  return  a  complex  result  if
  called with a complex argument. Try -10.0(Complex(x)).")
here is finally

其中,log()函数要求参数必须是正数,但提供的参数却是负数,所以会报错。运行中,异常发生时,类型DomainError()被记录在了变量ex中。再例如:

julia> except_test(x) = try
                            if 1 == x
                              log(-10)
                            else
                              x[2]
                            end
                          catch ex
                            if isa(ex, DomainError)         # 针对不同的类型做不同的处理
                              println("Catched DomainError")
                            elseif isa(ex, BoundsError)
                              println("Catched BoundsError")
                            end
                          end
except_test (generic function with 1 method)
julia> except_test(1)
Catched DomainError
julia> except_test(0)
Catched BoundsError

在异常处理的语法结构中,finally在对文件、网络等资源操作时比较有用。因为无论try代码块是否正常执行,还是catch代码块是否恰当地处理了异常,finally中的代码块都会执行,所以常用来关闭并释放各种资源,进行各种清理工作。例如:

        julia> f = open("file");

        julia> try
                  # 对文件对象f进行处理
                catch ex
                  # 异常处理
                finally
                  close(f)   # 关闭文件对象
                end

上例中,无论try是否顺利地对f进行了处理,close(f)都会执行。期间,若确实发生了异常,则先执行catch中的逻辑,然后也会自动流转到finally中。

在Julia的异常处理语法中,如果出现无法正确处理的错误,则可以通过rethrow()函数、backtrace()函数或catch_backtrace()函数对发现的异常再次传播,留给外部的代码进行处置,或者通过嵌套的try语法结构进行更深层的处理。

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!