Common lisp 按词汇设置函数符号

Common lisp 按词汇设置函数符号,common-lisp,lexical-scope,Common Lisp,Lexical Scope,我正在寻找一种简单、临时地交换函数的方法。 我知道我可以手动设置一个功能符号,如下所示: CL-USER> (setf (symbol-function 'abcd) #'+) #<FUNCTION +> CL-USER> (abcd 1 2 4) 7 有没有一种方法可以手动、按词汇设置函数名?例如: CL-USER> (some-variant-of-labels-or-let ((abcd #'*)) (abcd 1 2 4)) 8

我正在寻找一种简单、临时地交换函数的方法。 我知道我可以手动设置一个功能符号,如下所示:

CL-USER> (setf (symbol-function 'abcd) #'+)
#<FUNCTION +>
CL-USER> (abcd 1 2 4)
7
有没有一种方法可以手动、按词汇设置函数名?例如:

CL-USER> (some-variant-of-labels-or-let ((abcd #'*))
            (abcd 1 2 4))
8

注意:我试着戳标签和flet的来源,但它们都是特殊操作符。没有乐趣。

可以使用
符号函数修改的绑定不是词法绑定,因此这种选项实际上不适用。建立词汇绑定函数的唯一方法是通过标签和flet,因此必须使用它们。也就是说,使用宏可以很容易地获得所需的语法:

(defmacro bind-functions (binder bindings body)
  `(,binder ,(mapcar (lambda (binding)
                       (destructuring-bind (name function) binding
                         `(,name (&rest #1=#:args)
                                 (apply ,function #1#))))
                     bindings)
            ,@body))

(defmacro fflet ((&rest bindings) &body body)
  `(bind-functions flet ,bindings ,body))

(defmacro flabels ((&rest bindings) &body body)
  `(bind-functions labels ,bindings ,body))
fflet和flabel都使用函数指示符(符号或函数)并使用它们和任何附加参数调用apply。因此,您可以使用
#'*
'+

(fflet ((product #'*)
        (sum '+))
  (list (product 2 4)
        (sum 3 4)))
;=> (8 7)
这确实意味着您引入了
apply
的开销,但不清楚您可以做些什么来避免它。因为lambda表达式可以引用绑定名,所以我们可以允许这些引用指向新绑定的函数,或者指向外部的任何对象。这也是flet和labels之间的区别,这也是基于它们实现版本的原因:

(fflet ((double (lambda (x)
                  (format t "~&outer ~a" x)
                  (list x x))))
  (fflet ((double (lambda (x)
                    (format t "~&inner ~a" x)
                    (double x))))                           ; not recursive
    (double 2)))
; inner 2
; outer 2
;=> 2 2

选择 经过一段时间的思考,我意识到在Scheme中,
fflet
let
相同,因为Scheme是一个Lisp-1。要获得flabels的行为,必须在Scheme中使用
letrec
。搜索公共Lisp的
letrec
实现会得到一些有趣的结果

Robert Smith的描述和示例包括:

LETREC:LETREC是一个宏,旨在模仿Scheme的LETREC形式。 它是Common Lisp中函数式编程的有用结构, 这里有函数生成形式,需要在功能上 绑定到一个符号上

  (defun multiplier (n)
    (lambda (x) (* n x)))

  (letrec ((double (multiplier 2))
           (triple (multiplier 3)))
    (double (triple 5)))
  ;= 30
当然,这与apply有相同的问题,注释包括

不幸的是,宏不是一个非常有效的实现。那里 是函数调用的间接级别。本质上,一个 带绑定的LETREC

(name fn)
扩展为窗体的标签绑定

(name (&rest args)
  (apply fn args))
这有点糟糕

对于特定于实现的实现方法,欢迎使用补丁 宏


2005年,一个相当于Scheme的letrec的公共Lisp被指向标签

可以使用
符号函数修改的绑定不是词法绑定,因此这种选项实际上不适用。建立词汇绑定函数的唯一方法是通过标签和flet,因此必须使用它们。也就是说,使用宏可以很容易地获得所需的语法:

(defmacro bind-functions (binder bindings body)
  `(,binder ,(mapcar (lambda (binding)
                       (destructuring-bind (name function) binding
                         `(,name (&rest #1=#:args)
                                 (apply ,function #1#))))
                     bindings)
            ,@body))

(defmacro fflet ((&rest bindings) &body body)
  `(bind-functions flet ,bindings ,body))

(defmacro flabels ((&rest bindings) &body body)
  `(bind-functions labels ,bindings ,body))
fflet和flabel都使用函数指示符(符号或函数)并使用它们和任何附加参数调用apply。因此,您可以使用
#'*
'+

(fflet ((product #'*)
        (sum '+))
  (list (product 2 4)
        (sum 3 4)))
;=> (8 7)
这确实意味着您引入了
apply
的开销,但不清楚您可以做些什么来避免它。因为lambda表达式可以引用绑定名,所以我们可以允许这些引用指向新绑定的函数,或者指向外部的任何对象。这也是flet和labels之间的区别,这也是基于它们实现版本的原因:

(fflet ((double (lambda (x)
                  (format t "~&outer ~a" x)
                  (list x x))))
  (fflet ((double (lambda (x)
                    (format t "~&inner ~a" x)
                    (double x))))                           ; not recursive
    (double 2)))
; inner 2
; outer 2
;=> 2 2

选择 经过一段时间的思考,我意识到在Scheme中,
fflet
let
相同,因为Scheme是一个Lisp-1。要获得flabels的行为,必须在Scheme中使用
letrec
。搜索公共Lisp的
letrec
实现会得到一些有趣的结果

Robert Smith的描述和示例包括:

LETREC:LETREC是一个宏,旨在模仿Scheme的LETREC形式。 它是Common Lisp中函数式编程的有用结构, 这里有函数生成形式,需要在功能上 绑定到一个符号上

  (defun multiplier (n)
    (lambda (x) (* n x)))

  (letrec ((double (multiplier 2))
           (triple (multiplier 3)))
    (double (triple 5)))
  ;= 30
当然,这与apply有相同的问题,注释包括

不幸的是,宏不是一个非常有效的实现。那里 是函数调用的间接级别。本质上,一个 带绑定的LETREC

(name fn)
扩展为窗体的标签绑定

(name (&rest args)
  (apply fn args))
这有点糟糕

对于特定于实现的实现方法,欢迎使用补丁 宏


2005年,一个相当于Scheme的letrec的公共Lisp被指向标签

您可以使用以下设置全局定义函数的同义名称:

基于上述功能,我想提出这个宏:

(defmacro with-synonyms (params &body body)
  `(prog2 (setf ,@(mapcan (lambda (x)
                            `((symbol-function ',(car x)) #',(cadr x)))
                          params))
     (progn ,@body)
     ,@(mapcar (lambda (x) `(fmakunbound ',(car x)))
               params)))
它可以根据您的需要工作:

CL-USER> (with-synonyms ((product *) (sum +))
           (product 2 (sum 2 3)))
10
宏观扩张:

(PROG2 (SETF (SYMBOL-FUNCTION 'PRODUCT) #'*
             (SYMBOL-FUNCTION 'SUM)     #'+)
  (PROGN (PRODUCT 2 (SUM 2 3)))
  (FMAKUNBOUND 'PRODUCT)
  (FMAKUNBOUND 'SUM))
在宏体之外,没有诸如
product
sum
之类的函数

注意:这些同义函数仍然是全局定义的(虽然时间很短),因此这种解决方案并不理想


另外,实际上
(setf符号函数)
是非常非常邪恶的事情

CL-USER> (setf (symbol-function 'normal-plus) #'+)
#<SYSTEM-FUNCTION +>
CL-USER> (defun magic-plus (&rest rest)
           (if (every (lambda (x) (= 2 x)) rest)
               5
               (apply 'normal-plus rest)))
MAGIC-PLUS
CL-USER> (setf (symbol-function '+) #'magic-plus)
#<FUNCTION MAGIC-PLUS (&REST REST) (DECLARE (SYSTEM::IN-DEFUN MAGIC-PLUS))
  (BLOCK MAGIC-PLUS (IF (EVERY (LAMBDA (X) (= 2 X)) REST) 5
(APPLY 'NORMAL-PLUS REST)))>
CL-USER> (+ 2 3)
5
CL-USER> (+ 5 5)
10
CL-USER> (+ 2 2)
5
CL-USER>(setf(符号函数'normal plus)#+)
#
CL-USER>(defun magic plus和rest)
(如果(每(λ(x)(=2 x))剩余)
5.
(应用“正常加休息”))
魔术加号
CL-USER>(setf(符号函数'+)#'magic-plus)

# 您可以使用以下设置全局定义函数的同义名称:

基于上述功能,我想提出这个宏:

(defmacro with-synonyms (params &body body)
  `(prog2 (setf ,@(mapcan (lambda (x)
                            `((symbol-function ',(car x)) #',(cadr x)))
                          params))
     (progn ,@body)
     ,@(mapcar (lambda (x) `(fmakunbound ',(car x)))
               params)))
它可以根据您的需要工作:

CL-USER> (with-synonyms ((product *) (sum +))
           (product 2 (sum 2 3)))
10
宏观扩张:

(PROG2 (SETF (SYMBOL-FUNCTION 'PRODUCT) #'*
             (SYMBOL-FUNCTION 'SUM)     #'+)
  (PROGN (PRODUCT 2 (SUM 2 3)))
  (FMAKUNBOUND 'PRODUCT)
  (FMAKUNBOUND 'SUM))
在宏体之外,没有诸如
product
sum
之类的函数

注意:这些同义函数仍然是全局定义的(虽然时间很短),因此这种解决方案并不理想


另外,实际上
(setf符号函数)
是非常非常邪恶的事情

CL-USER> (setf (symbol-function 'normal-plus) #'+)
#<SYSTEM-FUNCTION +>
CL-USER> (defun magic-plus (&rest rest)
           (if (every (lambda (x) (= 2 x)) rest)
               5
               (apply 'normal-plus rest)))
MAGIC-PLUS
CL-USER> (setf (symbol-function '+) #'magic-plus)
#<FUNCTION MAGIC-PLUS (&REST REST) (DECLARE (SYSTEM::IN-DEFUN MAGIC-PLUS))
  (BLOCK MAGIC-PLUS (IF (EVERY (LAMBDA (X) (= 2 X)) REST) 5
(APPLY 'NORMAL-PLUS REST)))>
CL-USER> (+ 2 3)
5
CL-USER> (+ 5 5)
10
CL-USER> (+ 2 2)
5
CL-USER>(setf(符号函数'normal plus)#+)
#
CL-USER>(defun magic plus和rest)