flet vs let + funcall之间的区别

时间:2016-01-12 19:28:59

标签: common-lisp let

实际上这是两个相关问题。

  1. flet和let for binding函数有什么区别。
  2. 以下示例A和B是否相同?
  3. A)

    (flet ((myfun (x) (+ x 3)))
      (myfun 3))
    

    B)

    (let ((myfun (lambda (x) (+ x 3))))
      (funcall myfun 3))
    

    NB:

    (let ((myfun (lambda (x) (+ x 3))))
      (myfun 3))
    

    会抛出错误" myfun"未定义的运算符。

2 个答案:

答案 0 :(得分:4)

让我们从你的第二个问题开始:

  
      
  1. 以下示例A和B是否相同?
  2.   

正如@sds的答案详细讨论的那样,两者在语义上是等价的,即使大多数Common Lisp编译器都更有效地编译A(这很容易理解,因为B需要额外调用高级函数{{ 1}})。

现在,第一个问题:

  
      
  1. flet和let for binding functions之间有什么区别。
  2.   

一个不同之处在于,由于Common Lisp是Lisp-2语言(例如参见此question),因此示例A将函数funcall的名称绑定到功能对象,而第二个一个将正常的词法变量绑定到同一个功能对象。

结果是,从实际的角度来看,效率的一部分,语法(调用功能对象的更方便的方式,如你的示例所示)和语义(例如,该函数定义了一个命名块,因此可以编写类似于myfun的内容,而不能使用(flet ((myfun (x) (when (< x 0) (return-from myfun 0)) (+ x 3))) (myfun -4))

但也许最重要的一点是,let只是双特殊运算符fletflet的一部分,用于定义局部函数(与labels一起定义本地宏,请参阅specification)。使用macrolet运算符,您可以定义递归本地函数,这是let无法实现的。

答案 1 :(得分:3)

let创建新的变量绑定。

flet定义本地函数。

它们用于不同的目的。

您的示例执行相同的操作,但它们可能会以不同方式编译(示例A更多&#34;正统&#34;因此更有可能更高效)。例如,在CLISP中:

[1]> (disassemble '(flet ((myfun (x) (+ x 3))) (myfun 3)))

Disassembly of function :LAMBDA
(CONST 0) = 3
(CONST 1) = #<COMPILED-FUNCTION :LAMBDA-MYFUN>
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
4 byte-code instructions:
0     (CONST&PUSH 0)                      ; 3
1     (CONST 1)                           ; #<COMPILED-FUNCTION :LAMBDA-MYFUN>
2     (CALLC)
3     (SKIP&RET 1)
NIL
[2]> (disassemble '(let ((myfun (lambda (x) (+ x 3)))) (funcall myfun 3)))

Disassembly of function :LAMBDA
(CONST 0) = #<COMPILED-FUNCTION :LAMBDA-1>
(CONST 1) = 3
0 required arguments
0 optional arguments
No rest parameter
No keyword parameters
5 byte-code instructions:
0     (CONST&PUSH 0)                      ; #<COMPILED-FUNCTION :LAMBDA-1>
1     (LOAD&PUSH 0)
2     (CONST&PUSH 1)                      ; 3
3     (FUNCALL 1)
5     (SKIP&RET 2)
NIL

SBCL

*  (disassemble (lambda () (flet ((myfun (x) (+ x 3))) (myfun 3))))

; disassembly for (LAMBDA ())
; Size: 22 bytes. Origin: #x1003BD6D94
; 94:       498B4C2460       MOV RCX, [R12+96]                ; thread.binding-stack-pointer
                                                              ; no-arg-parsing entry point
; 99:       48894DF8         MOV [RBP-8], RCX
; 9D:       BA0C000000       MOV EDX, 12
; A2:       488BE5           MOV RSP, RBP
; A5:       F8               CLC
; A6:       5D               POP RBP
; A7:       C3               RET
; A8:       CC10             BREAK 16                         ; Invalid argument count trap
NIL
*  (disassemble (lambda () (let ((myfun (lambda (x) (+ x 3)))) (funcall myfun 3))))

; disassembly for (LAMBDA ())
; Size: 34 bytes. Origin: #x1003C40804
; 04:       498B4C2460       MOV RCX, [R12+96]                ; thread.binding-stack-pointer
                                                              ; no-arg-parsing entry point
; 09:       48894DF8         MOV [RBP-8], RCX
; 0D:       BA06000000       MOV EDX, 6
; 12:       488B0597FFFFFF   MOV RAX, [RIP-105]               ; #<FUNCTION (LAMBDA
                                                              ;                #) ..>
; 19:       B902000000       MOV ECX, 2
; 1E:       FF7508           PUSH QWORD PTR [RBP+8]
; 21:       FF60FD           JMP QWORD PTR [RAX-3]
; 24:       CC10             BREAK 16                         ; Invalid argument count trap
NIL

如您所见,flet版本效率更高。