为什么程序设计语言要加入 Exception 机制?这个问题的答案或许不是那么显然。
Exception 常见于 “操作过程可能出现意外” 的场景。比如,试图打开文件时发现文件不存在,或者试图访问数组元素时发现下标越界等等。有趣的是,C 语言中没有 Exception,同样的功能是靠 “特殊返回值” 实现的。比如,fopen 在打开文件失败时会返回 -1。
如果 Exception 的使用场景都可以用特殊返回值实现,Exception 有什么意义呢?下文试着给出我的回答。
“结构化程序设计” 思想把控制流限制为顺序、循环、分支三种。在有些问题中,只使用这些结构会导致信息流“绕远路”。例如,在程序的所有位置,除零都会引起算数中断,跳出任意多层循环。
借用 《The Seasoned Schemer》中的一个例子:
(define leftmost (lambda (l) (cond [(null? l) '()] [(leaf? (car l)) (car l)] [else (let ([a (leftmost (car l))]) (if (leaf? a) a (leftmost (cdr l))))])))函数 leftmost 求嵌套列表最左边的叶子元素。在发现叶子元素后,函数调用栈中已经积攒了不少栈帧,必须层层返回。期间,leaf? 被多次调用,做了无用功。如果 leaf? 是一个耗时的操作,矛盾将更加尖锐。
(define leftmost (lambda (l) (call/cc (lambda (skip) (letrec ([lm (lambda (l) (cond [(null? l) '()] [(leaf? (car l)) (skip (car l))] [else (begin (lm (car l)) (lm (cdr l)))]))]) (lm l))))))用 call/cc,在发现叶子元素时忽略调用栈,显式调用断点直接返回,解决上述问题。语义上,就是 Exception。
如何在与现实中使用 Exception(如设计使用 Exception 的函数库)仍然是个问题,因为这和我们切入问题的角度(函数的定义)有关。
不过,可以确定的是,Exception 不仅和函数设计有关,还和程序性能有关。正如 Exception 的字面意思(something that is not follow a rule or pattern),它引入特殊的控制流,形成信息流的捷径。
最后,我认为应当指出的是,如果不考虑性能问题,Exception 的使用(使用与否)应当以增强程序可读性、可维护性为目的。