我最近开始学习CL,我试图找出是否可以在函数名称空间中为符号设置值。
我想根据功能组合来定义函数,或者只是通过现有函数来定义函数(例如,我只需要在玩具“Hello,World!”级项目中重写identity
两次。我不确定它是否可能,因为它可能会破坏命名空间分离(至少没有强制执行)。我还尝试macroexpand
defun
实际做了什么;作为一个初学者,它对我来说太神秘了,但它似乎是内置的核心部分(我正在使用SBCL)。
所以,例如而不是说:
(defun myfn (x) x)
我只想说:
(def..? myfn identity)
如果我不能这样做,我怎么能避免这类代码重复? (包括由函数组合创建的函数,而不仅仅是创建同义词)。
答案 0 :(得分:5)
当前环境中可能存在绑定到符号的函数:
(setf (symbol-function 'myfn) #'identity)
还有(setf FDEFINTION)
大致相同,但也支持setf
个功能。
对于词法绑定函数(FLET
和LABELS
),我们无法做到这一点。
示例如何使用(setf fdefinition)
CL-USER 11 > (setf (fdefinition 'myfn) #'identity)
#<Function IDENTITY 410003F974>
CL-USER 12 > (myfn 10)
10
进一步评论
请注意,它在当前环境中执行此操作。因此
(defun myfn (x) x)
可能被
之类的东西取代(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (fdefinition 'myfn) #'identity))
Tha优势:在编译过程中,该功能将一如既往。
要记住的一件事:
符号对象可能带有一些信息。例如原始函数名称,如果有的话:
CL-USER 13 > #'identity
#<Function IDENTITY 410003F974>
设置符号的功能单元格不会改变:
CL-USER 14 > #'myfn
#<Function IDENTITY 410003F974>
它与原始内部名称相同的功能对象。一个人可能能够访问该名称,但无法改变它:
CL-USER 18 > (nth-value 2 (function-lambda-expression #'identity))
IDENTITY
自递归函数也存在问题:
CL-USER 19 > (defun foo (n)
(if (zerop n)
1
(* n (foo (1- n))))) ; here foo calls the function foo
FOO
现在我们将BAR
设置为使用函数FOO
CL-USER 20 > (setf (symbol-function 'bar) #'foo)
#<interpreted function FOO 4060004084>
CL-USER 21 > (bar 10)
3628800
我们在此重新定义FOO
:
CL-USER 22 > (defun foo (n)
(if (zerop n)
1
(+ n (foo (1- n)))))
FOO
BAR
仍然使用旧的FOO
,可能会也可能不会调用新的FOO
。
CL-USER 23 > (bar 10)
460
OOPS!我们更改了FOO,但它对BAR有奇怪的效果:第一次迭代是旧的FOO,下一次递归调用是新的FOO。
BAR
和FOO
是同一功能的两个不同版本。但是两者都调用函数FOO
,它可能是旧函数或新函数 - 取决于你如何解释或编译代码。
CL-USER 24 > #'foo
#<interpreted function FOO 40600041F4>
CL-USER 25 > #'bar
#<interpreted function FOO 4060004084>