Common lisp 用于参数检查和其他偏执狂的常见Lisp习语?
这个问题是关于编码约定、最佳实践和生产中的风格、任务关键型公共Lisp代码。我仔细阅读了Google的Common Lisp Style Guide(),但没有找到任何明确解决我的具体问题的方法,我通过示例和与C/C++、Java等的对比来表达。我还快速浏览了基于Github的常见Lisp代码,我没有看到很多参数检查和中间值检查,我在C/C++、Java、,等等 <> P>在我的商店里,我们习惯于检查论据和其他价值,并且在争论不符合合同的前提条件下采取早期退出方式。例如,考虑下面的(人为的,不完美的,典型的,但请不要浪费时间批评,微观例子,这表明CL例子): 问题是Common Lisp中的实际生产代码是否会进行更多检查。例如,编写显式代码来检查s实际上是一个字符串并且实际上足够长,并且前两个字符实际上是“O!”,这可能是合理的 这段代码是否仅仅因为它是教学性的,就绕过了所有的偏执狂?任务关键型生产部署中的同一代码是否更有可能进行偏执检查(我对Github的CL代码进行的轻微调查表明“否”)?如果现实世界的CL代码不倾向于偏执狂,为什么不呢?角落案例或穷举测试的实践是否比表面上更普遍Common lisp 用于参数检查和其他偏执狂的常见Lisp习语?,common-lisp,design-by-contract,Common Lisp,Design By Contract,这个问题是关于编码约定、最佳实践和生产中的风格、任务关键型公共Lisp代码。我仔细阅读了Google的Common Lisp Style Guide(),但没有找到任何明确解决我的具体问题的方法,我通过示例和与C/C++、Java等的对比来表达。我还快速浏览了基于Github的常见Lisp代码,我没有看到很多参数检查和中间值检查,我在C/C++、Java、,等等 P>在我的商店里,我们习惯于检查论据和其他价值,并且在争论不符合合同的前提条件下采取早期退出方式。例如,考虑下面的(人为的,不完美的
简言之,我对风格上的差异感到困惑。现实世界中,任务关键型C代码往往是超级偏执狂。我在CL中看不到同样的情况。也许我没有看到正确的代码库?也许我没有读对书?这个问题的答案似乎不容易在谷歌上找到。通用Lisp是一种专为开发大型复杂应用程序而设计的语言。在80年代被认为是大型应用。但它从生产系统中获得了一些处理错误的工具,甚至对编译时检查提供了一些支持。还有很多代码是为原型软件、研究系统和/或个人目的编写的。你不会发现质量总是很高的。还要记住,有时非常严格的检查可能会使代码过于严格(例如:许多HTTP客户机将发送不一致的请求,但事实就是这样,如果不丢失大量可能的用户,就无法轻松拒绝这些请求) 让我们来看一些例子,Common Lisp如何帮助您编写健壮的软件: strong键入和运行时类型检查 我们期望一个普通的Lisp系统会对每个操作进行运行时检查。避免使用不需要的Lisp系统 如果您有一个数值函数:
(defun foo (n x)
....
(bar ...))
(defun bar (a b)
(+ a b))
如果FOO
没有进行参数检查,我们希望+
操作最终会检查参数。在运行时将出现错误,并运行错误处理程序,默认情况下,该处理程序将调用调试器
想想看:所有(大多数)操作都将在运行时被检查。所有对象都有一个基本类型标记(整数、字符串、数组、位向量、字符、流等),并且在运行时最终将检查该类型
但我们对Lisp运行时的期望更高:
- 数组边界检查
- 插槽类型检查
- 发生错误时的堆一致性
- 针对有害操作的各种检查,如重新定义标准函数、删除公共Lisp包、算术错误等
)
参数列表
CommonLisp允许在编译时进行一些参数列表检查。使用它
(defun foo (&key (n 1) (x 1.0))
...)
现在,一个典型的编译器将捕获一个调用,如(foo:y2:x2.0)
,并出现一个错误:错误的关键字参数:y
让编译器检查参数列表中的参数数量是否正确,以及使用的关键字参数是否正确
CLOS,通用Lisp对象系统
使用CLOS
(defmethod foo ((n integer) (x float)) ...)
如果您像上面那样定义一个方法,那么在运行时,方法体中的n
将是一个整数,x
将是一个浮点。如果您使用其他参数类型调用FOO
,但没有应用任何方法,则会出现运行时错误
类似于示例插槽:您可以声明类型
(defclass bar ()
((x :type float)
(n :type integer)))
使用一个通用的Lisp实现来实际检查这些声明或编写自己的检查
另外:不要基于列表创建原始数据结构。始终将它们打包到CLOS类和方法中。这样,您就可以获得适当数量的运行时检查和内省功能
在运行时检查类型
Common Lisp为运行时类型检查提供了宏:
CHECK-TYPE
宏允许奇特的类型检查,甚至修复错误
CL-USER 27 > (foo 2000 5)
Error: The value 5 of X is not of type FLOAT.
1 (continue) Supply a new value of X.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 28 : 1 > :c 1
Enter a form to be evaluated: 5.0
请注意,您可以使用DEFTYPE
和任意类型谓词定义自己的类型
使用发出错误信号的Lisp结构
例如,ecase
与案例
:
CL-USER 37 > (let ((code 10))
(ecase code
(1 'fine)))
Error: 10 fell through ECASE expression.
Wanted one of (1).
ecase
在没有匹配的子句时自动发出错误信号
宏允许我们检查任意断言。
Common Lisp提供了一个内置宏
同样,可以进行一定量的运行时修复:
CL-USER 33 > (foo 2001 5.0)
Error: The assertion (AND (INTEGERP N) (EVENP N)) failed.
1 (continue) Retry assertion with new value for N.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 34 : 1 > :c 1
Enter a form to be evaluated:
2000
98.38699
现在我们可以编写一个额外的方法来检查n
是否大于x
:
(defmethod setup-bar :before ((b bar) (n1 integer) (x1 float))
(assert (> n x) (n x)))
:before方法将始终在主方法之前运行
将合同设计系统添加到CLOS中
这里有图书馆。这是一个exa
(check-type a1 (array * (3 3)))
CL-USER 37 > (let ((code 10))
(ecase code
(1 'fine)))
Error: 10 fell through ECASE expression.
Wanted one of (1).
(defun foo (n x)
(assert (and (integerp n) (evenp n)) (n))
(assert (floatp x) (x))
(* (isqrt n) (sqrt x)))
CL-USER 33 > (foo 2001 5.0)
Error: The assertion (AND (INTEGERP N) (EVENP N)) failed.
1 (continue) Retry assertion with new value for N.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 34 : 1 > :c 1
Enter a form to be evaluated:
2000
98.38699
(defclass bar ()
((n :type integer)
(x :type float)))
(defmethod setup-bar ((b bar) (n1 integer) (x1 float))
(with-slots (n x) b
(setf n n1 x x1))
b))
(defmethod setup-bar :before ((b bar) (n1 integer) (x1 float))
(assert (> n x) (n x)))
(define-condition mailer-incomplete-delivery-error
(mailer-error)
((recipient-and-status-list :initarg :recipient-and-status-list
:reader mailer-error-recipient-and-status-list)))