我可以通过它的值在函数名称空间中定义一个函数吗?

Mas*_*tic 2 common-lisp

我最近开始学习CL,我试图找出是否可以在函数名称空间中为符号设置值.

我想根据函数组合定义函数,或者只是通过现有函数定义函数(例如,我只需要identity在玩具"Hello,World!"级项目中重写两次).我不确定它是否可能,因为它可能会破坏命名空间分离(至少没有强制执行).我也尝试过实际做的macroexpand事情defun; 作为一个初学者,它对我来说太神秘了,但似乎内置的核心部分(我正在使用SBCL).

所以,例如,而不是说:

(defun myfn (x) x)
Run Code Online (Sandbox Code Playgroud)

我宁愿说:

(def..? myfn identity)
Run Code Online (Sandbox Code Playgroud)

如果我不能这样做,我怎么能避免这类代码重复?(包括由函数组合创建的函数,而不仅仅是创建同义词).

Rai*_*wig 5

在当前环境中,对于符号绑定的函数是可能的:

(setf (symbol-function 'myfn) #'identity)
Run Code Online (Sandbox Code Playgroud)

还有(setf FDEFINTION)哪些大致相同,但也支持setf功能.

对于词法绑定函数(by FLETLABELS),不能这样做.

示例如何使用设置符号函数 (setf fdefinition)

CL-USER 11 > (setf (fdefinition 'myfn) #'identity)
#<Function IDENTITY 410003F974>

CL-USER 12 > (myfn 10)
10
Run Code Online (Sandbox Code Playgroud)

进一步的评论

请注意,它在当前环境中执行此操作.从而

(defun myfn (x) x)
Run Code Online (Sandbox Code Playgroud)

可能会被类似的东西所取代

(eval-when (:compile-toplevel :load-toplevel :execute)
  (setf (fdefinition 'myfn) #'identity))
Run Code Online (Sandbox Code Playgroud)

优点:在编译期间,该函数将通常被称为.

要记住的一件事:

符号对象可以携带一些信息.例如原始函数名称,如果有的话:

CL-USER 13 > #'identity
#<Function IDENTITY 410003F974>
Run Code Online (Sandbox Code Playgroud)

设置符号的功能单元格不会改变:

CL-USER 14 > #'myfn
#<Function IDENTITY 410003F974>
Run Code Online (Sandbox Code Playgroud)

它与原始内部名称是相同的函数对象.一个人可能能够访问该名称,但无法更改它:

CL-USER 18 > (nth-value 2 (function-lambda-expression #'identity))
IDENTITY
Run Code Online (Sandbox Code Playgroud)

自递归函数也存在问题:

CL-USER 19 > (defun foo (n)
               (if (zerop n)
                   1
                 (* n (foo (1- n)))))  ; here foo calls the function foo
FOO
Run Code Online (Sandbox Code Playgroud)

现在我们设置BAR使用该功能FOO

CL-USER 20 > (setf (symbol-function 'bar) #'foo)
#<interpreted function FOO 4060004084>


CL-USER 21 > (bar 10)
3628800
Run Code Online (Sandbox Code Playgroud)

在这里我们重新定义FOO:

CL-USER 22 > (defun foo (n)
               (if (zerop n)
                   1
                 (+ n (foo (1- n)))))
FOO
Run Code Online (Sandbox Code Playgroud)

BAR仍然使用旧的FOO,可能会或可能不会调用新的FOO.

CL-USER 23 > (bar 10)
460
Run Code Online (Sandbox Code Playgroud)

OOPS!我们改变了FOO,但它对BAR有奇怪的影响:第一次迭代是旧的FOO,下一次递归调用是新的FOO.

BAR并且FOO是同一功能的两个不同版本.但是两者都调用函数FOO,可能是旧函数或新函数 - 取决于您如何解释或编译代码.

CL-USER 24 > #'foo
#<interpreted function FOO 40600041F4>

CL-USER 25 > #'bar
#<interpreted function FOO 4060004084>
Run Code Online (Sandbox Code Playgroud)