gpt4 book ai didi

common-lisp - 用于检查参数和其他偏执狂的常见Lisp习语?

转载 作者:行者123 更新时间:2023-12-03 12:21:00 27 4
gpt4 key购买 nike

这个问题与生产任务关键型Common-Lisp代码中的编码约定,最佳实践和样式有关。我细读了Google的Common-Lisp样式指南(http://tinyurl.com/qfvnqcx),但没有找到明确解决我特定问题的任何东西,我通过示例并与C / C ++,Java等进行了比较。我也快速浏览了一下。 Common-Lisp代码基于Github,我没有看到很多参数检查和中间值检查,而在C / C ++,Java等中却确实看到了。

在我的商店中,我们非常习惯检查参数和其他值,并在参数不符合合同/前提条件等情况下提早退出。例如,请考虑以下内容(人为,不完善,典型但请不要这样做)。批评t浪费时间的微型示例,以CL示例为准):

ErrorCode o_symb_to_g_symb (char * symb, uint len)
{ if (len < 2) { return ERROR_LENGTH; }
if (symb[0] != 'O' || symb[1] != '!') { return ERROR_SYNTAX; }
char * result = (char *) malloc (len + 1);
if (NULL == result) { return ERROR_MALLOC; }
if (result != strncpy (result, symb, len + 1))
{ return ERROR_STRNCPY; }
result[0] = 'G';
return result; }


这与第67页的“放任Lambda”中道格·霍伊特(Doug Hoyte)的代码大致相同,只是在执行过程中要尽可能多地检查( http://letoverlambda.com/)。

  (defun o!-symbol-to-g!-symbol (s)
(symb "G!"
(subseq (symbol-name s) 2))))


问题是,Common Lisp中的实际生产代码是否进行了更多检查。例如,编写显式代码来检查s是否确实是字符串并且实际上足够长,并且实际上具有“ O!”可能是合理的。作为其前两个字符。

这段代码是否仅出于教学目的而绕开了所有偏执狂?关键任务生产部署中的相同代码是否更有可能进行偏执检查(我在Github上轻描淡写地使用CL代码会提示“否”)?如果现实世界中的CL代码不倾向于偏执狂,那为什么不呢?极端情况或详尽测试的实践是否比看起来更普遍?

简而言之,我对样式的差异感到困惑。在现实世界中,关键任务C代码往往过于偏执。我在CL中看不到相同的内容。也许我没有在寻找正确的代码库?也许我没有读正确的书?谷歌搜索似乎不容易找到这个问题的答案。

最佳答案

Common Lisp是一种用于开发大型和复杂应用程序的语言。 80年代被认为是大型应用程序。但是它从生产系统中获得了一些用于处理错误的设施,甚至还提供了对编译时检查的支持。仍然为原型软件,研究系统和/或个人目的编写了大量代码。您不会总是找到高质量的产品。还要记住,有时非常严格的检查可能会使代码过于严格(例如:许多HTTP客户端将发送不符合要求的请求,但这就是这种方式,并且在不失去大量可能的用户的情况下,我们无法轻易拒绝它们)。

让我们看一些示例,Common Lisp如何帮助您编写强大的软件:

强大的类型输入和运行时类型检查

我们期望普通的Lisp系统将对每个操作进行运行时检查。避免使用Lisp系统。

如果您具有数字函数:

(defun foo (n x)
....
(bar ...))

(defun bar (a b)
(+ a b))


如果 FOO不进行任何参数检查,我们期望最终 +操作将检查参数。在运行时,将有一个错误和一个错误处理程序将运行,默认情况下,它将调用调试器。

考虑一下:所有(大多数)操作都将在运行时检查。所有对象都具有原始类型标签(整数,字符串,数组,位向量,字符,流等),并且在运行时最终将检查类型。

但是我们希望从Lisp运行时获得更多信息:


数组边界检查
插槽类型检查
发生错误时的堆一致性
各种有害操作检查,例如重新定义标准功能,删除Common Lisp软件包,算术错误等。


使用不执行运行时类型检查的Lisp系统非常麻烦。现在,Common Lisp允许我们声明部分代码不执行运行时检查。最佳策略:在不产生风险的情况下,找到最少的代码量(请参见 LOCALLY)。

参数列表

Common Lisp允许在编译时检查一些参数列表。用它。

(defun foo (&key (n 1) (x 1.0))
...)


现在,典型的编译器将捕获类似 (foo :y 2 :x 2.0)的调用,但出现错误:错误的关键字参数 :y

让编译器检查参数列表中的参数数目是否正确,以及是否使用了正确的关键字参数。

CLOS,通用Lisp对象系统

使用CLOS。

(defmethod foo ((n integer) (x float)) ...)


如果您定义了上述方法,则在运行时,方法主体 n将为整数,而 x将为浮点数。如果您使用其他参数类型调用 FOO且没有方法适用,那么我们会遇到运行时错误。

与实例插槽类似:您可以声明类型。

(defclass bar ()
((x :type float)
(n :type integer)))


使用Common Lisp实现,该实现实际上检查那些声明或编写您自己的检查。

另外:不要基于列表创建原始数据结构。始终将它们打包到CLOS类和方法中。这样,您可以获得适量的运行时检查和自检功能。

在运行时检查类型

Common Lisp为运行时类型检查提供了一个宏: CHECK-TYPE

(defun foo (n x)
(check-type n integer)
(check-type x float)
(* (isqrt n) (sqrt x)))


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


请注意,您可以使用这些类型来指定诸如数字间隔,数组维数或类似内容之类的内容。

例如,这检查绑定到变量 a1的对象是否是二维数组,其尺寸为3 x 3:

(check-type a1 (array * (3 3)))


请注意,您可以使用带有任意类型谓词的 DEFTYPE定义自己的类型。

使用表示错误的Lisp构造

例如 ecasecase

CL-USER 37 > (let ((code 10))
(ecase code
(1 'fine)))

Error: 10 fell through ECASE expression.
Wanted one of (1).


当没有子句匹配时, ecase自动发出错误信号。

ASSERT宏允许我们检查任意断言。

Common Lisp提供了内置的 ASSERT宏。

(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


使用CLOS按合同进行简单设计

(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))


现在我们可以编写一个额外的方法来检查例如 n大于 x

(defmethod setup-bar :before ((b bar) (n1 integer) (x1 float))
(assert (> n x) (n x)))


:before方法将始终在主方法之前运行。

将按合同设计系统添加到CLOS

有图书馆。 Quid Pro Quo是一个示例。 MatthiasHölzl还有一个更简单,更旧的DBC实现: Design by Contract

条件系统的高级错误处理

编写条件类型:

(define-condition mailer-incomplete-delivery-error
(mailer-error)
((recipient-and-status-list :initarg :recipient-and-status-list
:reader mailer-error-recipient-and-status-list)))


上面是基于 mailer-error条件的新条件。在运行时,我们可以捕获SMTP响应代码并发出这种情况的信号。

编写处理程序,然后重新启动以处理错误。那是先进的。条件系统的广泛使用通常表示更好的代码。

编写和检查测试

在许多情况下,健壮的代码需要测试套件。 Common Lisp也不例外。

让用户报告错误

在许多Common Lisp实现中,可以获取错误条件对象,回溯和一些环境数据。将它们写入错误日志。让用户报告那些。例如,LispWorks在调试器中具有 :bug-form命令。

关于common-lisp - 用于检查参数和其他偏执狂的常见Lisp习语?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34107079/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com