Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/visual-studio-code/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Common lisp 如何判断我的函数是否已经过尾部调用优化?_Common Lisp_Tail Call Optimization - Fatal编程技术网

Common lisp 如何判断我的函数是否已经过尾部调用优化?

Common lisp 如何判断我的函数是否已经过尾部调用优化?,common-lisp,tail-call-optimization,Common Lisp,Tail Call Optimization,我正在阅读中的第2.8节(尾部递归)。它有一个尾部递归函数的示例: (defun our-length-tr (lst) "tail recursive version with accumulator" (labels ((rec (lst acc) (if (null lst) acc (rec (cdr lst) (1+ acc))))) (rec lst 0)))

我正在阅读中的第2.8节(尾部递归)。它有一个尾部递归函数的示例:

(defun our-length-tr (lst)
  "tail recursive version with accumulator"
  (labels ((rec (lst acc)
             (if (null lst)
                 acc        
                 (rec (cdr lst) (1+ acc)))))
    (rec lst 0)))
它说许多常见的Lisp编译器都有TCO,但您可能需要在文件顶部添加
(宣告(优化速度))


我如何确定我的编译器支持TCO,并且它将把我的函数编译成循环版本而不是递归版本?

有几种简单的方法可以检查函数是否使用尾部递归编译

如果您可以阅读汇编语言,则可以使用原语函数
反汇编
(请参阅),例如:

* (disassemble 'our-length-tr)

; disassembly for OUR-LENGTH-TR
; Size: 89 bytes. Origin: #x10034F8434
; 34:       498B4C2460       MOV RCX, [R12+96]                ; no-arg-parsing entry point
                                                              ; thread.binding-stack-pointer
; 39:       48894DF8         MOV [RBP-8], RCX
; 3D:       488B4DF0         MOV RCX, [RBP-16]
; 41:       31D2             XOR EDX, EDX
; 43:       EB3E             JMP L2
; 45:       660F1F840000000000 NOP
; 4E:       6690             NOP
; 50: L0:   4881F917001020   CMP RCX, #x20100017              ; NIL
; 57:       7506             JNE L1
; 59:       488BE5           MOV RSP, RBP
; 5C:       F8               CLC
; 5D:       5D               POP RBP
; 5E:       C3               RET
; 5F: L1:   8D41F9           LEA EAX, [RCX-7]
; 62:       A80F             TEST AL, 15
; 64:       751F             JNE L3
; 66:       488B5901         MOV RBX, [RCX+1]
; 6A:       48895DE8         MOV [RBP-24], RBX
; 6E:       BF02000000       MOV EDI, 2
; 73:       41BBF004B021     MOV R11D, #x21B004F0             ; GENERIC-+
; 79:       41FFD3           CALL R11
; 7C:       488B5DE8         MOV RBX, [RBP-24]
; 80:       488BCB           MOV RCX, RBX
; 83: L2:   EBCB             JMP L0
; 85: L3:   0F0B0A           BREAK 10                         ; error trap
; 88:       2F               BYTE #X2F                        ; OBJECT-NOT-LIST-ERROR
; 89:       08               BYTE #X08                        ; RCX
; 8A:       0F0B10           BREAK 16                         ; Invalid argument count trap
NIL
(Mac OS X 10.13.3上的SBCL 1.4.1)

否则,您可以使用很长的列表调用该函数,并查看结果是堆栈溢出错误(递归编译为递归),还是列表的长度(递归编译为迭代,尾部递归)

更好的是,您可以提供无限长的列表,如:

(our-length-tr '#1=(1 2 3 . #1#)))

并查看是否产生了堆栈溢出错误(通常几乎是立即产生的),或者由于迭代的无限循环而根本不产生任何输出。

有两种简单的方法可以检查函数是否使用尾部递归编译

如果您可以阅读汇编语言,则可以使用原语函数
反汇编
(请参阅),例如:

* (disassemble 'our-length-tr)

; disassembly for OUR-LENGTH-TR
; Size: 89 bytes. Origin: #x10034F8434
; 34:       498B4C2460       MOV RCX, [R12+96]                ; no-arg-parsing entry point
                                                              ; thread.binding-stack-pointer
; 39:       48894DF8         MOV [RBP-8], RCX
; 3D:       488B4DF0         MOV RCX, [RBP-16]
; 41:       31D2             XOR EDX, EDX
; 43:       EB3E             JMP L2
; 45:       660F1F840000000000 NOP
; 4E:       6690             NOP
; 50: L0:   4881F917001020   CMP RCX, #x20100017              ; NIL
; 57:       7506             JNE L1
; 59:       488BE5           MOV RSP, RBP
; 5C:       F8               CLC
; 5D:       5D               POP RBP
; 5E:       C3               RET
; 5F: L1:   8D41F9           LEA EAX, [RCX-7]
; 62:       A80F             TEST AL, 15
; 64:       751F             JNE L3
; 66:       488B5901         MOV RBX, [RCX+1]
; 6A:       48895DE8         MOV [RBP-24], RBX
; 6E:       BF02000000       MOV EDI, 2
; 73:       41BBF004B021     MOV R11D, #x21B004F0             ; GENERIC-+
; 79:       41FFD3           CALL R11
; 7C:       488B5DE8         MOV RBX, [RBP-24]
; 80:       488BCB           MOV RCX, RBX
; 83: L2:   EBCB             JMP L0
; 85: L3:   0F0B0A           BREAK 10                         ; error trap
; 88:       2F               BYTE #X2F                        ; OBJECT-NOT-LIST-ERROR
; 89:       08               BYTE #X08                        ; RCX
; 8A:       0F0B10           BREAK 16                         ; Invalid argument count trap
NIL
(Mac OS X 10.13.3上的SBCL 1.4.1)

否则,您可以使用很长的列表调用该函数,并查看结果是堆栈溢出错误(递归编译为递归),还是列表的长度(递归编译为迭代,尾部递归)

更好的是,您可以提供无限长的列表,如:

(our-length-tr '#1=(1 2 3 . #1#)))

并查看是否产生了堆栈溢出错误(通常几乎是立即产生的),或者由于迭代的无限循环而根本没有产生任何输出。

@Renzo这可能应该作为一个答案来写一些常见的Lisp编译器会这样做,一些不会这样做,一些只是在影响
跟踪和调试时才会这样做。通常情况下,人们应该使用declare或declaim而不是declaim。函数本身内部的
(declare(optimize speed))
应该具有相同的效果。请参阅CL实现中的TCO:@Renzo这可能应该作为一个答案来编写一些常见的Lisp编译器可以这样做,一些不可以这样做,有些只是在影响
跟踪和调试时才这样做。通常情况下,人们应该使用declare或declaim而不是declaim。函数本身内部的
(declare(optimize speed))
应该具有相同的效果。请参阅关于CL实现中的TCO:Nifty infinite list!漂亮的无限列表!