温馨提示:本文翻译自stackoverflow.com,查看原文请点击:其他 - Common Lisp difference between declare check-type
common-lisp declare

其他 - 声明检查类型之间的常见Lisp区别

发布于 2020-04-23 16:52:11

有人可以解释一下以下两种情况之间的区别吗(特别是对于我来说,如果我无法理解的话,这些评论是怎么说的),它们来自功能方面的CLHS

;; This function assumes its callers have checked the types of the
;; arguments, and authorizes the compiler to build in that assumption.
(defun discriminant (a b c)
  (declare (number a b c))
  "Compute the discriminant for a quadratic equation."
  (- (* b b) (* 4 a c))) =>  DISCRIMINANT
(discriminant 1 2/3 -2) =>  76/9

;; This function assumes its callers have not checked the types of the
;; arguments, and performs explicit type checks before making any assumptions. 
(defun careful-discriminant (a b c)
  "Compute the discriminant for a quadratic equation."
  (check-type a number)
  (check-type b number)
  (check-type c number)
  (locally (declare (number a b c))
    (- (* b b) (* 4 a c)))) =>  CAREFUL-DISCRIMINANT
(careful-discriminant 1 2/3 -2) =>  76/9

查看更多

提问者
Student
被浏览
26
Rainer Joswig 2020-02-13 22:39

CHECK-TYPE:运行时类型检查和修复

check-type进行实际的运行时检查。通常,它还提供了一种交互式确定值的方法。

* (let ((a "1"))
    (check-type a number)
    (+ a 2))

debugger invoked on a SIMPLE-TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {10005184C3}>:
  The value of A is "1", which is not of type NUMBER.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [STORE-VALUE] Supply a new value for A.
  1: [ABORT      ] Exit debugger, returning to top level.

(SB-KERNEL:CHECK-TYPE-ERROR A "1" NUMBER NIL)
0] 0

Enter a form to be evaluated: 1
3

DECLARE:类型声明

Common Lisp是动态类型的:每个数据对象都有一个类型。

Common Lisp另外还允许人们将静态类型用于变量和函数。

  • 各种类型和复合类型
  • 定义新类型的方法 deftype
  • 类型声明与 declare
  • 子类型检查 subtypep
  • 运行时类型检查 typep
  • 运行时类型与条件typecasectypecaseetypecase

现在,Common Lisp实现将类型声明用于各种事物,并且它们对它们的处理高度依赖于实现。

(declare (type ...))Common Lisp编译器中静态类型声明的主要用法是:

  • 无视他们。通常,Lisp 解释器和某些编译器会完全忽略它们

  • 使用它们进行速度和空间优化。这是由许多编译器完成的。他们可以使用这些类型声明来创建专门的代码。

  • 使用它们进行运行时类型检查。一些实现将类型声明用于运行时检查。

  • 使用它们进行编译时类型检查。一些实现将类型声明用于编译时类型检查。例子是sbcl和cmucl。

请注意,Common Lisp标准没有说明如何使用这些类型声明。它只是提供了一种定义和声明类型的语法。然后,常见的Lisp实现要么使用它们,要么忽略它们。

可以在SBCL和CMUCL中找到特别复杂的类型声明用法。

类型检查示例

让我们看看SBCL如何在运行时和编译时类型检查中使用类型声明:

使用SBCL进行运行时类型检查:

* (defun add (a b)
    (declare (type number a b))
    (list a b))
ADD
* (add 1 "3")

debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {10005184C3}>:
  The value
    "3"
  is not of type
    NUMBER
  when binding B

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(ADD 1 "3") [external]
   source: (SB-INT:NAMED-LAMBDA ADD
               (A B)
             (DECLARE (TYPE NUMBER A B))
             (BLOCK ADD (LIST A B)))
0] 

如我们所见,SBCL使用类型声明进行运行时检查。但是有别于check-type它并不能提供不同的值和相应的重新检查...

使用SBCL进行编译时类型检查:

* (defun subtract (a b)
    (declare (type number a b))
    (concatenate 'string a "-" b " is " (- a b)))

; in: DEFUN SUBTRACT
;     (CONCATENATE 'STRING A "-" B " is " (- A B))
; 
; caught WARNING:
;   Derived type of (SB-KERNEL:SYMEVAL 'A) is
;     (VALUES NUMBER &OPTIONAL),
;   conflicting with its asserted type
;     SEQUENCE.
;   See also:
;     The SBCL Manual, Node "Handling of Types"
; 
; compilation unit finished
;   caught 1 WARNING condition
SUBTRACT

如您所见,我们正在尝试使用数字作为序列。SBCL在编译时检测到该情况并发出警告。