处理数学函数中的错误 [英] Handling errors in math functions

查看:160
本文介绍了处理数学函数中的错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在数学相关功能中,什么是错误处理的良好做法?我正在构建一个专门的函数库(module),我的主要目的是为调试这些函数的代码调试更容易 - 不要使用户友好的错误处理工具。



以下是VBA中的一个简单示例,但我也有兴趣从其他语言听到。我不太确定我应该在哪里返回错误信息/状态/标志。作为一个额外的论据?

 函数AddArrays(arr1,arr2)
Dim i As Long
Dim result As Variant

'这里有一些错误捕获代码,例如
' - 是相同大小的输入数组吗?
' - 输入数组是否为数字? (不能添加字符串,对象...)
' - 等等

'如果没有发现错误,做实际工作...
ReDim结果(LBound(对于UBound(arr1))
对于i = LBound(arr1)到UBound(arr1)
result(i)= arr1(i)+ arr2(i)
Next i

AddArrays = result
结束函数

或类似于以下内容。该函数返回一个布尔成功标志(如下面的示例,如果输入数组不是数字等),返回False或其他类型的错误号/消息。

 函数AddArrays(arr1,arr2,result)As Boolean 

'
$ b AddArrays = booSuccess

结束功能

但是这不是太疯狂了,因为它遗漏了好的可读取的语法,即不能再说 c = AddArrays(a,b)



我接受建议!

解决方案

显然错误处理一般来说,这是一个很大的话题,最好的做法取决于您正在使用的语言的功能,以及编码程序如何与其他例程相匹配。所以我将约束我对VBA(Excel中使用的)和您所描述的类型的库类型例程的答案。



异常与错误代码在图书馆例程中



在这种情况下,我不会使用返回码。 VBA支持一种异常处理的形式,虽然并不像C ++ / Java / ?? .NET中更为标准的形式那样强大,但却非常相似。所以这些语言的建议一般都适用。您使用异常来告诉调用例程,被调用的例程不能以任何原因做它的工作。您可以处理最低级别的异常,您可以对此失败做一些有意义的事情。



Bjarne Stroustrup给出了一个非常好的解释,为什么异常比这种错误代码更好这本书的情况。 (本书是关于C ++,但C ++异常处理和VBA错误处理背后的原则是一样的)。



http://www2.research.att.com/~bs/3rd.html



这是第8.3节的一个很好的摘录:


当程序由单独的
模块组成时,特别是当这些
模块来自单独开发的
库时,错误处理需要
分为两个不同的部分:[1]
报告错误条件
无法在本地解决[2]
处理其他地方检测到的错误
库的作者可以检测
运行时错误,但一般来说,
有什么想法要做什么关于他们。
图书馆的用户可能知道如何
处理这样的错误,但不能
检测它们 - 否则他们将在用户的代码中处理
而不是
留给图书馆查找。


第14.1和14.9节还解决了库上下文中的异常与错误代码。 (在archive.org上有一本书的副本。)



在stackoverflow上可能有更多关于这一点。我刚刚发现这个,例如:



异常与错误代码与断言



(可能存在涉及正确管理必须清理的资源的陷阱当使用例外,但是它们并不真的适用于此。)



VBA中的异常



这是在VBA中如何引发异常(尽管VBA术语是引发错误):

 函数AddArrays(arr1,arr2)
Dim i As Long
Dim result As Variant

'这里有一些错误查找代码,例如
' - 是相同大小的输入数组吗?
' - 输入数组是否为数字? (不能添加字符串,对象...)
' - 等等

'假设errorsFound是一个您上面填充的变量...
如果errorsFound然后
调用Err.Raise(SOME_BAD_INPUT_CONSTANT)'请参阅有关VBA Err对象的帮助。 (SOME_BAD_INPUT_CONSTANT是你要定义的东西)
如果

'如果没有找到错误,请执行实际工作...
ReDim结果(LBound(arr1))到UBound (arr1))
对于i = LBound(arr1)到UBound(arr1)
result(i)= arr1(i)+ arr2(i)
下一个i

AddArrays = result
结束函数

如果此例程没有捕获错误, VBA将在调用堆栈中给它上面的其他例程一个机会(见: VBA错误气泡)。以下是呼叫者如何做:

 公共函数addExcelArrays(a1,a2)
错误转到EH

addExcelArrays = AddArrays(a1,a2)

退出函数

EH:

'ERR_VBA_TYPE_MISMATCH未定义VBA,但它的价值是13 ...
如果Err.Number = SOME_BAD_INPUT_CONSTANT或者Err.Number = ERR_VBA_TYPE_MISMATCH然后

'我们预计这可能会经常发生...
addExcelArrays = CVErr(xlErrValue)
Else

'我们不知道发生了什么...
调用debugAlertUnexpectedError()'这是你要定义的
结束如果
结束函数

做某事有意义意味着取决于你的申请。在上面我的调用者示例的情况下,决定通过返回Excel可以放在工作表单元格中的错误值来处理一些错误,而其他错误值则需要令人讨厌的警报。 (以下是Excel中VBA的情况实际上不是一个坏的具体示例,因为许多应用程序区分了内部和外部例程,以及期望能够处理的异常和您只想知道的错误条件之间的区别但是你没有回应。)



不要忘记断言



因为你提到调试,还值得注意的是断言的作用。如果您希望AddArrays只有通过实际创建自己的数组或通过其他方式验证的例程才能使用数组,那么可以这么做:

 函数AddArrays(arr1,arr2)
Dim i As Long
Dim result As Variant

Debug.Assert IsArray(arr1)
Debug.Assert IsArray(arr2)

'其余的代码...
结束函数

对断言和异常之间的区别进行了一个很好的讨论:



Debug.Assert与异常投掷



我在这里给了一个例子:



断言是邪恶的?



关于一般数组处理例程的一些VBA建议



最后,作为VBA特定的注释,该re是VBA变体和数组有一些陷阱,当你想编写一般的库例程时,必须避免。数组可能有多个维度,它们的元素可能是对象或其他数组,它们的起始和结束索引可能是任何东西,等等。这是一个例子(未经测试,并不是穷尽的),其中介绍了一些: p>

 '注意:这没有经过测试,并不一定是详尽的!这只是一个例子! 
函数addArrays(arr1,arr2)

'请注意使用一些其他库函数,你可能有...
'* isVect(v)只有当v为数组,只有一个
'维度
'* lengthOfArr(v)返回第一个维度中的数组的大小
'* check(condition,errNum)引发了一个Err错误。 Number = errNum如果
'条件为False

'断言您假设您的呼叫者(这是您的
'应用程序的一部分)已经完成 - 即您假定呼叫者创建
'的输入,或者已经处理了严重错误的输入
Debug.Assert isVect(arr1)
Debug.Assert isVect(arr2)
Debug.Assert lengthOfArr(arr1 )= lengthOfArr(arr2)
Debug.Assert lengthOfArr(arr1)> 0

'VBA数组索引弹性地址的帐号

ReDim结果(1到lengthOfArr(arr1))As Double
Dim indResult As Long

Dim ind1 As Long
ind1 = LBound(arr1)

Dim ind2 As Long
ind2 = LBound(arr2)

Dim v1
Dim v2

对于indResult = 1 To lengthOfArr(arr1)

'注意范围到值的隐含胁迫。请注意,如果没有默认属性的对象分配给
'变体,则VBA将引发
'的错误。
v1 = arr1(ind1)
v2 = arr2(ind2)

'如果我们有任何非数字,则提高错误。 (不要将数字文本作为数字计算一个字符串
')。
调用检查(IsNumeric(v1)和VarType(v1)vbString,xlErrValue)
调用检查(IsNumeric(v2)和VarType(v2)<> vbString,xlErrValue)

'现在我们不希望这会引发错误。
result(indResult)= v1 + v2

ind1 = ind1 + 1
ind2 = ind2 + 1
下一个indResult

addArrays =结果
结束函数


What is good practice for error handling in math-related functions? I'm building up a library (module) of specialized functions and my main purpose is to make debugging easier for the code calling these functions -- not to make a shiny user-friendly error handling facility.

Below is a simple example in VBA, but I'm interested in hearing from other languages as well. I'm not quite sure where I should be returning an error message/status/flag. As an extra argument?

Function AddArrays(arr1, arr2)
    Dim i As Long
    Dim result As Variant

    ' Some error trapping code here, e.g.
    ' - Are input arrays of same size?
    ' - Are input arrays numeric? (can't add strings, objects...)
    ' - Etc.

    ' If no errors found, do the actual work...
    ReDim result(LBound(arr1) To UBound(arr1))
    For i = LBound(arr1) To UBound(arr1)
        result(i) = arr1(i) + arr2(i)
    Next i

    AddArrays = result
End Function

or something like the following. The function returns a boolean "success" flag (as in the example below, which would return False if the input arrays weren't numeric etc.), or an error number/message of some other type.

Function AddArrays(arr1, arr2, result) As Boolean

    ' same code as above

    AddArrays = booSuccess

End Function

However I'm not too crazy about this, since it ruins the nice and readable calling syntax, i.e. can't say c = AddArrays(a,b) anymore.

I'm open to suggestions!

解决方案

Obviously error handling in general is a big topic, and what the best practice is depends a lot on the capabilities of the language you're working with and how the routine you're coding fits in with other routines. So I'll constrain my answer to VBA (used within Excel) and library-type routines of the sort you're describing.

Exceptions vs. Error Codes in Library Routines

In this case, I would not use a return code. VBA supports a form of exception handling that, while not as powerful as the more standard form found in C++/Java/??.NET, is pretty similar. So the advice from those languages generally applies. You use exceptions to tell calling routines that the called routine can't do it's job for whatever reason. You handle exceptions at the lowest level where you can do something meaningful about that failue.

Bjarne Stroustrup gives a very good explanation of why exceptions are better than error codes for this kind of situation in this book. (The book is about C++, but the principles behind C++ exception handling and VBA error handling are the same.)

http://www2.research.att.com/~bs/3rd.html

Here is a nice excerpt from Section 8.3:

When a program is composed of separate modules, and especially when those modules come from separately developed libraries, error handling needs to be separated into two distinct parts: [1] The reporting of error conditions that cannot be resolved locally [2] The handling of errors detected elsewhere The author of a library can detect runtime errors but does not in general have any idea what to do about them. The user of a library may know how to cope with such errors but cannot detect them – or else they would be handled in the user’s code and not left for the library to find.

Sections 14.1 and 14.9 also address exceptions vs. error codes in a library context. (There is a copy of the book online at archive.org.)

There is probably lots more about this on stackoverflow. I just found this, for example:

Exception vs. error-code vs. assert

(There can be pitfalls involving proper management of resources that must be cleaned up when using exceptions, but they don't really apply here.)

Exceptions in VBA

Here is how raising an exception looks in VBA (although the VBA terminology is "raising an error"):

Function AddArrays(arr1, arr2) 
    Dim i As Long 
    Dim result As Variant 

    ' Some error finding code here, e.g. 
    ' - Are input arrays of same size? 
    ' - Are input arrays numeric? (can't add strings, objects...) 
    ' - Etc. 

    'Assume errorsFound is a variable you populated above...
    If errorsFound Then
        Call Err.Raise(SOME_BAD_INPUT_CONSTANT)    'See help about the VBA Err object. (SOME_BAD_INPUT_CONSTANT is something you would have defined.)
    End If

    ' If no errors found, do the actual work... 
    ReDim result(LBound(arr1) To UBound(arr1)) 
    For i = LBound(arr1) To UBound(arr1) 
        result(i) = arr1(i) + arr2(i) 
    Next i 

    AddArrays = result 
End Function

If this routine doesn't catch the error, VBA will give other routines above it in the call stack a chance to (See this: VBA Error "Bubble Up"). Here is how a caller might do so:

Public Function addExcelArrays(a1, a2)
    On Error Goto EH

    addExcelArrays = AddArrays(a1, a2)

    Exit Function

EH:

    'ERR_VBA_TYPE_MISMATCH isn't defined by VBA, but it's value is 13...
    If Err.Number = SOME_BAD_INPUT_CONSTANT Or Err.Number = ERR_VBA_TYPE_MISMATCH Then

        'We expected this might happen every so often...
        addExcelArrays = CVErr(xlErrValue)
    Else

        'We don't know what happened...
        Call debugAlertUnexpectedError()    'This is something you would have defined
    End If
End Function

What "do something meaningful" means depends on the context of your application. In the case of my caller example above, it decides that some errors should be handled by returning an error value that Excel can put in a worksheet cell, while others require a nasty alert. (Here's where the case of VBA within Excel is actually not a bad specific example, because lots of applications make a distinction between internal and external routines, and between exceptions you expect to be able to handle and error conditions that you just want to know about but for which you have no response.)

Don't Forget Assertions

Because you mentioned debugging, it's also worth noting the role of assertions. If you expect AddArrays to only ever be called by routines that have actually created their own arrays or otherwise verified they are using arrays, you might do this:

Function AddArrays(arr1, arr2) 
    Dim i As Long 
    Dim result As Variant 

    Debug.Assert IsArray(arr1)
    Debug.Assert IsArray(arr2)

    'rest of code...
End Function

A fantastic discussion of the difference between assertions and exceptions is here:

Debug.Assert vs Exception Throwing

I gave an example here:

Is assert evil?

Some VBA Advice About General Array Handling Routines

Finally, as a VBA-specific note, there are VBA variants and arrays come with a number of pitfalls that must be avoided when you're trying to write general library routines. Arrays might have more than one dimension, their elements might be objects or other arrays, their start and end indices might be anything, etc. Here is an example (untested and not trying to be exhaustive) that accounts for some of that:

'NOTE: This has not been tested and isn't necessarily exhaustive! It's just
'an example!
Function addArrays(arr1, arr2)

    'Note use of some other library functions you might have...
    '* isVect(v) returns True only if v is an array of one and only one
    '  dimension
    '* lengthOfArr(v) returns the size of an array in the first dimension
    '* check(condition, errNum) raises an error with Err.Number = errNum if
    '  condition is False

    'Assert stuff that you assume your caller (which is part of your
    'application) has already done - i.e. you assume the caller created
    'the inputs, or has already dealt with grossly-malformed inputs
    Debug.Assert isVect(arr1)
    Debug.Assert isVect(arr2)
    Debug.Assert lengthOfArr(arr1) = lengthOfArr(arr2)
    Debug.Assert lengthOfArr(arr1) > 0

    'Account for VBA array index flexibility hell...

    ReDim result(1 To lengthOfArr(arr1)) As Double
    Dim indResult As Long

    Dim ind1 As Long
    ind1 = LBound(arr1)

    Dim ind2 As Long
    ind2 = LBound(arr2)

    Dim v1
    Dim v2

    For indResult = 1 To lengthOfArr(arr1)

        'Note implicit coercion of ranges to values. Note that VBA will raise
        'an error if an object with no default property is assigned to a
        'variant.
        v1 = arr1(ind1)
        v2 = arr2(ind2)

        'Raise errors if we have any non-numbers. (Don't count a string
        'with numeric text as a number).
        Call check(IsNumeric(v1) And VarType(v1) <> vbString, xlErrValue)
        Call check(IsNumeric(v2) And VarType(v2) <> vbString, xlErrValue)

        'Now we don't expect this to raise errors.
        result(indResult) = v1 + v2

        ind1 = ind1 + 1
        ind2 = ind2 + 1
    Next indResult

    addArrays = result
End Function

这篇关于处理数学函数中的错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆