Error handling 当有限范围变量的值超出范围时,是否可以覆盖Common Lisp中的类型错误行为?
抱歉,如果问题的主题措辞奇怪(因为缺乏更好的术语——这也是我在谷歌上找不到任何东西搜索这个特定主题的原因之一),下面是我的示例 假设此函数foobar已定义为:Error handling 当有限范围变量的值超出范围时,是否可以覆盖Common Lisp中的类型错误行为?,error-handling,integer,range,common-lisp,Error Handling,Integer,Range,Common Lisp,抱歉,如果问题的主题措辞奇怪(因为缺乏更好的术语——这也是我在谷歌上找不到任何东西搜索这个特定主题的原因之一),下面是我的示例 假设此函数foobar已定义为: (defun foobar (x) (declare (type (integer -100 100) x)) (format T "X is ~A~%" x)) 因此,对于declare行,x是一个整数,必须是-100、100或介于两者之间的任何整数。因此,这样做会产生一个错误: CL-USER> (foobar 10
(defun foobar (x)
(declare (type (integer -100 100) x))
(format T "X is ~A~%" x))
因此,对于declare行,x是一个整数,必须是-100、100或介于两者之间的任何整数。因此,这样做会产生一个错误:
CL-USER> (foobar 101)
The value 101 is not of type (INTEGER -100 100).
[Condition of type TYPE-ERROR]
Restarts:
(blah blah blah)
除了将函数本身更改为显式执行钳制外,是否有一种方法可以指定重写行为,以便在不更改foobar本身的defun的情况下执行此操作:
(foobar [any-value-over-100])
将其钳制为100,同样地,x<-100,而函数体本身没有额外的代码行
编辑:回答一个响应者,这是钳制——将值严格保持在定义的最小和最大范围内。在Lisp中,这是一个示例:
CL-USER> (defun clamp (x min max)
(if (> x max)
max
(if (< x min)
min
x)))
CLAMP
CL-USER> (clamp 5 4 9)
5
CL-USER> (clamp -2 4 9)
4
CL-USER> (clamp 123 4 9)
9
<代码>CL-USER>(拆卸夹具(x最小最大值)
(如果(>x最大值)
最大值
(如果((defparameter *foobar-orig* (fdefinition *foobar*))
(defun foobar (x)
(funcall *foobar-orig* (whatever-you-want x)))
(defun ensure-range (x low high)
(cond ((< x low) low)
((> x high) high)
(t x)))
(defun foobar (x)
(let (x (ensure-range x -100 100))
(format T "X is ~A~%" x)))
使用重新启动
您最好的方法是用声明替换声明并建立适当的声明,例如
该标准不提供全局错误处理程序,而是提供实现。如果我理解正确,您希望确保整数在该范围内,如果是这种情况,我认为您不应该使用类型错误来处理它,而是(let…)类似于以下内容:
(defparameter *foobar-orig* (fdefinition *foobar*))
(defun foobar (x)
(funcall *foobar-orig* (whatever-you-want x)))
(defun ensure-range (x low high)
(cond ((< x low) low)
((> x high) high)
(t x)))
(defun foobar (x)
(let (x (ensure-range x -100 100))
(format T "X is ~A~%" x)))
(解除锁定确保范围(x低-高)
(条件(x高)高)
(t x)))
(十)
(让(x(确保范围x-100))
(格式T“X为~A~%”X)
公共Lisp中的类型声明
您的代码:
(defun foobar (x)
(declare (type (integer -100 100) x))
(format T "X is ~A~%" x))
上面用(foobar 120)
之类的东西调用的结果在Common Lisp中完全没有定义
- 它可能被完全忽略
- 它可能会导致错误或各种运行时问题
- 它可以帮助编译器创建更好的代码(顺便说一句,这是这些声明的主要原因)
- 可以在编译/运行时对其进行类型检查。只有很少的Lisp编译器能做到这一点
check-type
或ASSERT
(defun foobar (x)
(check-type x (integer -100 100))
(format T "X is ~A~%" x))
建议
在Lisp中,在不更改源代码的情况下扩展函数称为“建议”。这不是普通函数的通用Lisp标准,但应该有相应的工具,编写这样的东西并不难
扩展通用函数
CommonLisp为通用函数内置了这种机制。标准方法组合有:before、:after和:about
(defmethod foobar ((x integer))
(check-type x (integer -100 100))
(format T "X is ~A~%" x))
在CommonLisp中,不能对任意类型进行分派—只能对类进行分派。有一些基本类型的类,如字符串、整数等。。。这里我们使用x
是一个整数
如果要夹紧foobar
的x
:
(defmethod foobar :around ((x integer))
(call-next-method (clamp x -200 100)))
上面是一个:around
方法。我调用下一个方法,即上面的方法,并更改参数。只要参数类不更改分派,这是允许的
替代方法:宏观
一个目标可能是编写更少的代码,让代码更具声明性
也许有人想写:
(defun-clamped foobar ((x (integer :min -100 :clampled-max 100)))
(format T "X is ~A~%" x))
然后我只需编写defun-clipped
宏,它将扩展为一个正常的defun
,执行必要的操作。如果可以接受其他内容,请不要声明x介于-100和100之间。我认为,如果违反声明,实现可能允许任何类型的内存损坏。
那么,做什么呢
(declare (optimize (safety 0)))
避免声明抛出错误并不是一个好主意。
您可以先钳制该值,然后将函数定义的其余部分放入LET形式
(defun foo (x)
(declare (type integer x))
(let ((x (clamp x -100 100)))
(declare (type (integer -100 100) ; LET allows declarations, too!
x))
(bar x)))
如果希望宏为您执行此操作,则应使用以下类似的方法:
(defmacro defclamp (name (arg min max)
&body body)
`(defun ,name (,arg)
(declare (type real ,arg))
(let ((,arg (clamp ,arg ,min ,max)))
(declare (type (real ,min ,max)
,arg))
,@body)))
我无法在这个回复中输入正确格式的代码,所以我编辑了我的原始帖子来解释钳制功能。我不希望忽略类型声明,但如果整数超出范围整数类型声明的边界,则它的行为会有所不同,而不是调用错误。我更喜欢(INTEGER[min][max])类型声明,因为它有助于编译器进行某些优化,也有助于在REPL上执行(描述“foobar”)时的整个“自文档化代码”思想,这将忠实地描述该参数的类型声明。我认为我将采用的解决方案是将您的宏建议与sds提到的(declare(optimize(safety 0))结合起来,至少在SBCL中,安全设置为0允许我为具有范围值参数的函数指定该参数的越界值,而不会发生错误,并且在使用descripe函数时仍然显示正确的(整数-100)类型声明。因此,正如你所建议的,我可以编写一个定制的“增强型DeFun”宏,它定义了0个安全设置的函数,然后将自定义的箝位代码插入到函数的序幕中。虽然我看到了“安全性”,但我没有考虑内存损坏。