Common lisp flet与let之间的差异+;芬考尔

Common lisp flet与let之间的差异+;芬考尔,common-lisp,let,Common Lisp,Let,这实际上是两个相关的问题 对于绑定函数,flet和let之间有什么区别 下面的示例A和B是否等效 (A) (B) 注意: 将抛出错误“myfun”未定义运算符。创建新的变量绑定 定义本地函数 它们有不同的用途 您的示例执行相同的操作,但它们的编译方式可能不同(示例A更“正统”,因此更有可能更有效)。例如,在: [1]>(反汇编(flet((myfun(x)(+x3)))(myfun 3))) 函数的反汇编:LAMBDA (常数0)=3 (常数1)=# 0个必需参数 0个可选参数 无静止参数 无关

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

  • 对于绑定函数,flet和let之间有什么区别
  • 下面的示例A和B是否等效
  • (A)

    (B)

    注意:

    将抛出错误“myfun”未定义运算符。

    创建新的变量绑定

    定义本地函数

    它们有不同的用途

    您的示例执行相同的操作,但它们的编译方式可能不同(示例A更“正统”,因此更有可能更有效)。例如,在:

    [1]>(反汇编(flet((myfun(x)(+x3)))(myfun 3)))
    函数的反汇编:LAMBDA
    (常数0)=3
    (常数1)=#
    0个必需参数
    0个可选参数
    无静止参数
    无关键字参数
    4字节代码指令:
    0(CONST&PUSH 0);3.
    1(常数1)#
    2(CALLC)
    3(跳过和返回1)
    无
    [2] >(反汇编“(let((myfun(lambda(x)(+x3))))(funcall myfun 3)))
    函数的反汇编:LAMBDA
    (常数0)=#
    (常数1)=3
    0个必需参数
    0个可选参数
    无静止参数
    没有关键字参数
    5字节代码指令:
    0(CONST&PUSH 0)#
    1(加载和推送0)
    2(CONST&PUSH 1);3.
    3(全部1)
    5(跳过和返回2)
    无
    
    或:

    *(反汇编(lambda()(flet((myfun(x)(+x3)))(myfun 3)))
    ; 用于(LAMBDA())的反汇编
    ; 大小:22字节。产地:#x1003BD6D94
    ; 94:498B4C2460 MOV RCX,[R12+96];thread.binding-stack-pointer
    ; 没有arg解析入口点
    ; 99:48894DF8 MOV[RBP-8],RCX
    ; 9D:BA0000000 MOV EDX,12
    ; A2:488BE5 MOV RSP,RBP
    ; A5:F8 CLC
    ; A6:5D POP RBP
    ; A7:C3 RET
    ; A8:CC10断路器16;参数计数陷阱无效
    无
    *(分解(lambda()(let((myfun)(lambda(x)(+x3)))(funcall myfun 3)))
    ; 用于(LAMBDA())的反汇编
    ; 大小:34字节。产地:#x1003C40804
    ; 04:498B4C2460 MOV RCX,[R12+96];thread.binding-stack-pointer
    ; 没有arg解析入口点
    ; 09:48894DF8 MOV[RBP-8],RCX
    ; 0D:BA06000000 MOV EDX,6
    ; 12:488B0597FFFFFF MOV RAX[RIP-105]#
    ; 19:B902000000 MOV ECX,2
    ; 1E:FF7508推送QWORD PTR[RBP+8]
    ; 21:FF60FD JMP QWORD PTR[RAX-3]
    ; 24:CC10-16;参数计数陷阱无效
    无
    

    如您所见,flet的效率更高。

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

  • 下面的示例A和B是否等效
  • 正如@sds的答案详细讨论的那样,两者在语义上是等价的,即使A由大多数常见的Lisp编译器更有效地编译(这很容易理解,因为B需要额外调用高级函数
    funcall

    现在,关于你的第一个问题:

  • 对于绑定函数,flet和let之间有什么区别
  • 一个不同之处是,由于Common Lisp是一种Lisp-2语言(参见示例),示例a将function
    myfun
    的名称绑定到函数对象,而第二个示例将普通词汇变量绑定到同一个函数对象

    其结果是,从实用的角度来看,效率的一部分,在语法(调用函数对象的更方便的方式,如您的示例所示)和语义(例如,函数定义了一个命名块,因此可以编写如下内容:
    (flet((myfun)(x)(when(
    ,使用
    let
    )不可能


    但也许最重要的一点是,
    flet
    只是两个特殊操作符
    flet
    的一部分,
    labels
    用于定义本地函数(与
    macrolet
    一起定义本地宏,请参阅)使用
    标签
    操作符,您可以定义递归局部函数,这在let中是不可能的。

    第一个示例应该是
    (flet((myfun(x)(+x3)))(myfun 3))
    -这不是我在A下编写的吗)?不,您的第一个版本与NB之后的代码相同。您可以在修订历史记录中对此进行检查。您必须在匿名lambda中显式创建一个
    ,以启用从
    提前返回的
    ,因此这不是不可能的,而是不方便的。
    
    (flet ((myfun (x) (+ x 3)))
      (myfun 3))
    
    (let ((myfun (lambda (x) (+ x 3))))
      (funcall myfun 3))
    
    (let ((myfun (lambda (x) (+ x 3))))
      (myfun 3))
    
    [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
    
    *  (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