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)