5.4.1 异常触发
由于程序设计时,每段代码的正确执行都需要特定的前置条件,而通常这些条件是可知的。一旦条件不满足,会导致后续主要逻辑的不正常或无法运行,为此可以对这些可预知的“错误”进行提前设定处置逻辑,这些分支逻辑即是异常处理逻辑。
另外,既然是可知的错误,就可以预先对某些常见的错误进行定义,以便于后续的跟踪与处置。Julia中内置定义了很多错误类型,均继承自抽象类型Exception。本书在附录A中列出了常见的异常类型,更多的可参见官方文档。
对于这些预定义的错误,可在需要的时候通过throw()函数显式地抛出,以上报该处代码发生的问题,并方便后续的调试与处置。
例如有一个函数只能处理非负数,却收到了负数参数,如下所示:
julia> f(x) = x>=0 ? exp(-x) : throw(DomainError("x<0")) # 当x<0时抛出DomainError异常 f (generic function with 1 method) julia> f(1) 0.36787944117144233 julia> f(-1) # 负数作为参数 ERROR: DomainError with x<0: Stacktrace: [1] f(::Int64) at .\REPL[1]:1 [2] top-level scope at none:0
可见一旦运行条件不符合要求,DomainError的实例便会被抛出,同时会传递关于异常的描述信息。
提示 在异常信息之后,同时会有Stacktrace字段给出错误发生的具体函数原型及其定义的位置。因为该字段主要提示错误发生的位置,所以每次原型都会有所差异,本书因为篇幅,在下面的示例中出现错误时会省略该字段,仅展示ERROR信息。
除了预定义的错误类型,还有一个通用的异常类型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> @info "information message" [ Info: information message julia> @warn "warning message" ┌ Warning: warning message └ @ Main REPL[40]:1
关于日志方面,Julia有一个Logging包,提供了更为全面的功能,开发者可以根据需要安装使用。通过在代码恰当的位置输出对应的信息,能够在程序的编写、调试及测试方面带来很大的帮助。