关于异常处理:何时在Matlab中使用assert()?

When to use assert() in Matlab?

由于Matlab被解释,通常会在执行函数签名的函数开头花费大量时间。 例如

1
2
3
if nargin ~= 2; error('must provide two input args a and b'); end
if a < 0||a ~=floor(a); error('input arg1 must be positive non-zero integer'); end
if ~isa(b,'cell') ...

相反,使用Matlab的assert()会更好吗? 如果没有,何时适合在Matlab中使用assert()?

关于在生产代码中使用assert的讨论非常多,但我不确定这是否适用于解释代码。 同样,这里有另一个很好的讨论,我同意@Dan Dyer关于断言表达对当前状态的看法。 然而,在这里看一下类似的Python讨论,人们说,只有在不应该发生的情况下使用断言(例如异常情况的例外情况),这有点矛盾了w.r.t。 以前的参考文献。

也许这更像是一个关于断言在解释语言中扮演的角色的问题,而不是关于Matlab的问题。


在大多数情况下,两者之间没有区别

1
assert(X,...)

1
2
3
if (~X)
    error(...)
end

你在他们之间的选择是方便或风格的问题。

基于MATLAB的项目中非生产和生产代码之间的区别通常与基于其他语言的项目中的区别不同。

这部分是因为,正如你所说,MATLAB通常是解释而不是编译;尽管可以使用MATLAB Compiler或Builder产品生成应用程序,虽然没有严格"编译",但是没有可见的源代码且无法调试。对于那些类型的应用程序,您需要像编译语言一样谨慎处理异常和错误。

部分原因还在于"生产"通常意味着使用MATLAB的项目与其他语言的项目不同;例如,它可能意味着MATLAB代码自动转换为C以部署到汽车引擎控制器,或者它可能意味着某些MATLAB代码正在运行财务预测模型并将结果写入生产数据库。

有一种特殊情况应该使用assert而不是if..error..end,这是当你使用MATLAB Coder从MATLAB代码生成C代码时。 MATLAB编码器检查MATLAB代码中的assert语句,以推断它需要转换为C的变量的属性,并且如果它可以假设有关您断言的变量的事实(例如数组大小和类型),则可以生成更好的C代码。

最后一点:对于你提到的具体活动,强制执行函数签名,我不会使用任何方法 - inputParser通常更健壮和一致(虽然有点冗长),但更重要的是它鼓励你很好地设计函数签名首先。


此问题的另一个视角是运行时间。
通常,您希望您的代码能够正常运行。这意味着你不关心Matlab产生错误所需的几毫秒,而只关心逻辑测试。

这是我用来比较的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function o=test

a = 2;
o = [0 0];

tic;
if a~=2
error('a is not 2')
end
o(1) = toc;


tic
assert(a==2,'a is not 2')
o(2) = toc;

现在,让我们运行这个函数足够的时间来获得好的统计数据:

1
2
3
4
for i=1:10000
o(i,:) = test;
end
mean(o)

结果:

ans =

1.0e-05 *

0.0088 0.3548

底线:

断言比if-else慢得多。


MATLAB处理断言的方式意味着,从用户的角度来看,语句之间没有区别:

1
2
3
if error_check == false
    error('function:state','Error message');
end

1
assert(error_check==false,'function:state','Error message');

两者都导致向用户显示相同的输出,并且相同的数据存储在lasterr中。两者都被try-catch块捕获。基于一个非常快速和肮脏的测试,我暂时声称asserterror慢约5%(尽管这并没有真正在大型方案中产生巨大差异)。因此,唯一真正的区别在于代码的程序员/维护者,这使得它在很大程度上成为一种风格选择。

asserterror都有其优点和缺点。 assert看起来更清洁,占用的空间更少,但乍看之下并不是很明显。对于我个人使用,我更喜欢error的情况,因为我可以在错误调用的行上放置一个断点,并且只有在将要抛出错误时它才会中断 - 这不能作为容易和断言消息一样(你可以使用dbstop in file if error,但这可能有它自己的问题)。我还可以在if-statement中粘贴调试代码,以便在抛出错误之前打印有关程序状态的信息。