实际上这是两个相关问题。
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"未定义的运算符。
答案 0 :(得分:4)
让我们从你的第二个问题开始:
- 以下示例A和B是否相同?
醇>
正如@sds的答案详细讨论的那样,两者在语义上是等价的,即使大多数Common Lisp编译器都更有效地编译A(这很容易理解,因为B需要额外调用高级函数{{ 1}})。
现在,第一个问题:
- flet和let for binding functions之间有什么区别。
醇>
一个不同之处在于,由于Common Lisp是Lisp-2语言(例如参见此question),因此示例A将函数funcall
的名称绑定到功能对象,而第二个一个将正常的词法变量绑定到同一个功能对象。
结果是,从实际的角度来看,效率的一部分,语法(调用功能对象的更方便的方式,如你的示例所示)和语义(例如,该函数定义了一个命名块,因此可以编写类似于myfun
的内容,而不能使用(flet ((myfun (x) (when (< x 0) (return-from myfun 0)) (+ x 3))) (myfun -4))
。
但也许最重要的一点是,let
只是双特殊运算符flet
,flet
的一部分,用于定义局部函数(与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
版本效率更高。