common-lisp - 声明检查类型之间的 Common Lisp 区别

标签 common-lisp declare

有人可以解释以下两种情况之间的区别吗(如果我无法理解的话,特别是评论所说的内容)来自 function 上的 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

最佳答案

我正在尝试自己学习一些 CL,所以我会提供最好的答案。 Common Lisp 是一个 dynamic language与静态语言相比。对于静态语言,请查看 Haskell - 它会进行大量编译时检查以确保所有函数的类型匹配,并让您知道它是否失败。然而,在 Common Lisp 中,things are a little different .

However, in Common Lisp, variables aren't typed the way they are in languages such as Java or C++. That is, you don't need to declare the type of object that each variable can hold. Instead, a variable can hold values of any type and the values carry type information that can be used to check types at runtime. Thus, Common Lisp is dynamically typed--type errors are detected dynamically. For instance, if you pass something other than a number to the + function, Common Lisp will signal a type error. On the other hand, Common Lisp is a strongly typed language in the sense that all type errors will be detected--there's no way to treat an object as an instance of a class that it's not.



所以我们声明为函数参数的变量默认没有类型。这对您来说可能是一本好书:https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node15.html .在第一段中,它是这样写的:

It is important to note that in Lisp it is data objects that are typed, not variables. Any variable can have any Lisp object as its value. (It is possible to make an explicit declaration that a variable will in fact take on one of only a limited set of values. However, such a declaration may always be omitted, and the program will still run correctly. Such a declaration merely constitutes advice from the user that may be useful in gaining efficiency. See declare.)



所以无论何时你创建你的函数,那些使用的变量都可以有任何 Lisp 对象作为它的值。

如果我们在 declare 冒险我们看到以下内容:

There are two distinct uses of declare , one is to declare Lisp variables as "special" (this affects the semantics of the appropriate bindings of the variables), and the other is to provide advice to help the Common Lisp system (in reality the compiler) run your Lisp code faster, or with more sophisticated debugging options.



最后,如果我们看 check-type我们看:

check-type signals a correctable error of type type-error if the contents of place are not of the type typespec.



在声明和检查类型的两种情况下,我们都提供了有关类型和类型检查的 Common Lisp 系统建议。让我们看看您提供的两个示例函数。

首先,“判别式”函数使用声明函数来断言参数确实是数字并且编译器不需要检查它们。小心判别函数使用 check-type 来确保每个变量确实是一个数字,然后执行操作。

您可能会问“我为什么要为此烦恼?”,答案是提供更优化的函数(判别式)或提供更好的调试和更多错误信息的函数(小心判别式)。为了显示差异,我启动了 SBCL 并定义了两个函数。然后,我使用反汇编来显示每个的机器码。请注意仔细判别如何执行比判别更多的检查,从而导致更多的机器代码!

(反汇编#'判别式)
; disassembly for DISCRIMINANT
; Size: 83 bytes. Origin: #x10023700D7                        ; DISCRIMINANT
; 0D7:       498B5D10         MOV RBX, [R13+16]               ; thread.binding-stack-pointer
; 0DB:       48895DF8         MOV [RBP-8], RBX
; 0DF:       840425F8FF1020   TEST AL, [#x2010FFF8]           ; safepoint
; 0E6:       488B55E8         MOV RDX, [RBP-24]
; 0EA:       488B7DE8         MOV RDI, [RBP-24]
; 0EE:       FF1425C0000020   CALL QWORD PTR [#x200000C0]     ; GENERIC-*
; 0F5:       488955D8         MOV [RBP-40], RDX
; 0F9:       488B55F0         MOV RDX, [RBP-16]
; 0FD:       BF08000000       MOV EDI, 8
; 102:       FF1425C0000020   CALL QWORD PTR [#x200000C0]     ; GENERIC-*
; 109:       488B7DE0         MOV RDI, [RBP-32]
; 10D:       FF1425C0000020   CALL QWORD PTR [#x200000C0]     ; GENERIC-*
; 114:       488BFA           MOV RDI, RDX
; 117:       488B55D8         MOV RDX, [RBP-40]
; 11B:       FF1425B8000020   CALL QWORD PTR [#x200000B8]     ; GENERIC--
; 122:       488BE5           MOV RSP, RBP
; 125:       F8               CLC
; 126:       5D               POP RBP
; 127:       C3               RET
; 128:       CC10             INT3 16                         ; Invalid argument count trap
NIL

(反汇编#'小心判别)
; disassembly for CAREFUL-DISCRIMINANT
; Size: 422 bytes. Origin: #x10023701E3                       ; CAREFUL-DISCRIMINANT
; 1E3:       4D8B4510         MOV R8, [R13+16]                ; thread.binding-stack-pointer
; 1E7:       4C8945F8         MOV [RBP-8], R8
; 1EB:       840425F8FF1020   TEST AL, [#x2010FFF8]           ; safepoint
; 1F2:       EB44             JMP L1
; 1F4:       660F1F840000000000 NOP
; 1FD:       0F1F00           NOP
; 200: L0:   488B7DF0         MOV RDI, [RBP-16]
; 204:       4883EC10         SUB RSP, 16
; 208:       488B1571FFFFFF   MOV RDX, [RIP-143]              ; 'A
; 20F:       488B3572FFFFFF   MOV RSI, [RIP-142]              ; 'NUMBER
; 216:       4C894DD8         MOV [RBP-40], R9
; 21A:       488B056FFFFFFF   MOV RAX, [RIP-145]              ; #<SB-KERNEL:FDEFN SB-KERNEL:CHECK-TYPE-ERROR>
; 221:       B906000000       MOV ECX, 6
; 226:       48892C24         MOV [RSP], RBP
; 22A:       488BEC           MOV RBP, RSP
; 22D:       FF5009           CALL QWORD PTR [RAX+9]
; 230:       4C8B4DD8         MOV R9, [RBP-40]
; 234:       488955F0         MOV [RBP-16], RDX
; 238: L1:   840425F8FF1020   TEST AL, [#x2010FFF8]           ; safepoint
; 23F:       488B45F0         MOV RAX, [RBP-16]
; 243:       448D40F1         LEA R8D, [RAX-15]
; 247:       41F6C001         TEST R8B, 1
; 24B:       7512             JNE L2
; 24D:       4180F80A         CMP R8B, 10
; 251:       740C             JEQ L2
; 253:       41F6C00F         TEST R8B, 15
; 257:       75A7             JNE L0
; 259:       8078F129         CMP BYTE PTR [RAX-15], 41
; 25D:       77A1             JNBE L0
; 25F: L2:   EB47             JMP L4
; 261:       660F1F840000000000 NOP
; 26A:       660F1F440000     NOP
; 270: L3:   488B7DE8         MOV RDI, [RBP-24]
; 274:       4883EC10         SUB RSP, 16
; 278:       488B1519FFFFFF   MOV RDX, [RIP-231]              ; 'B
; 27F:       488B3502FFFFFF   MOV RSI, [RIP-254]              ; 'NUMBER
; 286:       4C894DD8         MOV [RBP-40], R9
; 28A:       488B05FFFEFFFF   MOV RAX, [RIP-257]              ; #<SB-KERNEL:FDEFN SB-KERNEL:CHECK-TYPE-ERROR>
; 291:       B906000000       MOV ECX, 6
; 296:       48892C24         MOV [RSP], RBP
; 29A:       488BEC           MOV RBP, RSP
; 29D:       FF5009           CALL QWORD PTR [RAX+9]
; 2A0:       4C8B4DD8         MOV R9, [RBP-40]
; 2A4:       488955E8         MOV [RBP-24], RDX
; 2A8: L4:   840425F8FF1020   TEST AL, [#x2010FFF8]           ; safepoint
; 2AF:       488B45E8         MOV RAX, [RBP-24]
; 2B3:       448D40F1         LEA R8D, [RAX-15]
; 2B7:       41F6C001         TEST R8B, 1
; 2BB:       7512             JNE L5
; 2BD:       4180F80A         CMP R8B, 10
; 2C1:       740C             JEQ L5
; 2C3:       41F6C00F         TEST R8B, 15
; 2C7:       75A7             JNE L3
; 2C9:       8078F129         CMP BYTE PTR [RAX-15], 41
; 2CD:       77A1             JNBE L3
; 2CF: L5:   EB3D             JMP L7
; 2D1:       660F1F840000000000 NOP
; 2DA:       660F1F440000     NOP
; 2E0: L6:   498BF9           MOV RDI, R9
; 2E3:       4883EC10         SUB RSP, 16
; 2E7:       488B15B2FEFFFF   MOV RDX, [RIP-334]              ; 'C
; 2EE:       488B3593FEFFFF   MOV RSI, [RIP-365]              ; 'NUMBER
; 2F5:       488B0594FEFFFF   MOV RAX, [RIP-364]              ; #<SB-KERNEL:FDEFN SB-KERNEL:CHECK-TYPE-ERROR>
; 2FC:       B906000000       MOV ECX, 6
; 301:       48892C24         MOV [RSP], RBP
; 305:       488BEC           MOV RBP, RSP
; 308:       FF5009           CALL QWORD PTR [RAX+9]
; 30B:       4C8BCA           MOV R9, RDX
; 30E: L7:   840425F8FF1020   TEST AL, [#x2010FFF8]           ; safepoint
; 315:       458D41F1         LEA R8D, [R9-15]
; 319:       41F6C001         TEST R8B, 1
; 31D:       7513             JNE L8
; 31F:       4180F80A         CMP R8B, 10
; 323:       740D             JEQ L8
; 325:       41F6C00F         TEST R8B, 15
; 329:       75B5             JNE L6
; 32B:       418079F129       CMP BYTE PTR [R9-15], 41
; 330:       77AE             JNBE L6
; 332: L8:   4C894DD8         MOV [RBP-40], R9
; 336:       488B55E8         MOV RDX, [RBP-24]
; 33A:       488B7DE8         MOV RDI, [RBP-24]
; 33E:       FF1425C0000020   CALL QWORD PTR [#x200000C0]     ; GENERIC-*
; 345:       488955E0         MOV [RBP-32], RDX
; 349:       4C8B4DD8         MOV R9, [RBP-40]
; 34D:       488B55F0         MOV RDX, [RBP-16]
; 351:       BF08000000       MOV EDI, 8
; 356:       FF1425C0000020   CALL QWORD PTR [#x200000C0]     ; GENERIC-*
; 35D:       4C8B4DD8         MOV R9, [RBP-40]
; 361:       498BF9           MOV RDI, R9
; 364:       FF1425C0000020   CALL QWORD PTR [#x200000C0]     ; GENERIC-*
; 36B:       488BFA           MOV RDI, RDX
; 36E:       4C8B4DD8         MOV R9, [RBP-40]
; 372:       488B55E0         MOV RDX, [RBP-32]
; 376:       FF1425B8000020   CALL QWORD PTR [#x200000B8]     ; GENERIC--
; 37D:       4C8B4DD8         MOV R9, [RBP-40]
; 381:       488BE5           MOV RSP, RBP
; 384:       F8               CLC
; 385:       5D               POP RBP
; 386:       C3               RET
; 387:       CC10             INT3 16                         ; Invalid argument count trap
NIL

如这里所见,Common Lisp 也可以编译,这让一些人感到困惑。最好在这里回答:How is Lisp dynamic and compiled? .

关于common-lisp - 声明检查类型之间的 Common Lisp 区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60104101/

相关文章:

linux - 如何在普通的 lisp 中设置网络服务器?

c - 声明一个结构数组

c++ - (Qt C++) 用值填充 QMap 并同时声明/初始化?

lisp - JSCL - 为什么会出现这个神秘的错误?

sockets - 如何使用 SBCL 的套接字重用之前打开的端口?

opengl - 使用常用的 lisp opengl 保存和加载图像

lisp - (= 1/5 0.2) 是假的?

c++ - 未在此范围内使用友好函数声明

javascript - typescript 声明第三方模块

Javascript 检查变量是否已声明但未赋值