我想在一类匿名函数中添加一些元数据,以便于识别。到目前为止,我的所有想法都是黑客或可疑的:
一个plist项目可以工作,但显然它只适用于符号,而不是lambdas。
我可以在docstring中插入一些内容:
(lambda (x) "is-special" ...
但这看起来很难看,或者:
(lambda (x)
(if
(eq x 'special)
t
...
甚至更丑。
在函数对象上设置属性是否有一种不那么糟糕的hackish方式?
我也可以使用类型系统来区分我的功能:
可以将函数放在结构或CLOS对象中,但感觉很麻烦。
是否可以创建作为函数类型的自定义子类型的lambda?
可以使CLOS对象充当可执行函数吗?我正在考虑类似于python的调用属性。
答案 0 :(得分:3)
我还不知道MOP还没有回答你的上一个问题,但我可以建议你做一个你想要的hackish方式。
您可以声明一个特殊变量来跟踪已添加元数据的lambda:(defvar *annotated-lambdas* (make-hash-table))
。
然后定义一个函数来从中获取/设置lambda。
(defun get-lambda (lambda &optional (annotated-lambdas *annotated-lambdas*))
(gethash lambda annotated-lambdas))
(defun (setf get-lambda) (annotation lambda &optional (annotated-lambdas *annotated-lambdas*))
(setf (gethash lambda annotated-lambdas) annotation))
现在你已经可以测试一下了:
(let ((l (lambda (x) (1+ x))))
(setf (get-lambda l) "Add one."))
此时*annotated-lambdas*
应该已经包含一个元素。
现在你可以使用一个小宏来减少麻烦:
(defmacro lambda* ((&rest args) (annotation &optional (annotated-lambdas *annotated-lambdas*)) &body body)
(let ((name (gensym)))
`(let ((,name (lambda (,@args)
,@body)))
(setf (get-lambda ,name ,annotated-lambdas) ,annotation))))
您可以这样使用:
(lambda* (x) ("Add one.")
(+1 x))
当然,要访问注释,您需要访问lambda,我假设您这样做,否则您将使用一个函数(它只是一个指向lambda的符号)。 “注释”也可以是任何东西,字符串,列表,alist,plist甚至哈希表。
答案 1 :(得分:3)
如果是打印,我会这样做:
CL-USER 8 > (flet ((foo (a b) (+ a b))) #'foo)
#<interpreted function FOO 40600008E4>
CL-USER 9 > (defmacro nlambda (name &rest rest)
`(flet ((,name ,@rest))
(function ,name)))
NLAMBDA
CL-USER 10 > (mapcar (lambda (x) (nlambda foo (y) (+ x y))) '(1 2 3 4))
(#<interpreted function FOO 4060000B54>
#<interpreted function FOO 4060000B84>
#<interpreted function FOO 4060000BB4>
#<interpreted function FOO 4060000BE4>)
如果您想要取回名称,可以使用FUNCTION-LAMBDA-EXPRESSION
,如果您的实现和编译器设置允许的话。在这里,我使用LispWorks。第三个值是名称:
CL-USER 30 > (flet ((foo (a b) (+ a b))) #'foo)
#<interpreted function FOO 406000183C>
CL-USER 31 > (nth-value 2 (function-lambda-expression *))
FOO
有关其他方法,请参阅:pretty function。
答案 2 :(得分:1)
编辑:一个快速而肮脏的选项,如果您的实现打印出flet
或labels
函数的名称:
(defmacro nlambda (name (&rest lambda-list) &body body)
`(labels ((,name (,@lambda-list)
,@body))
#',name))
如果需要可靠地打印功能名称,请继续阅读。
在您的上一个问题之后的一个示例,假设您的Common Lisp实现遵循元对象协议(MOP):
(defclass funcallable-object ()
((name :initarg :name :initform nil :accessor funcallable-name)
(function :initarg :function :initform nil :accessor funcallable-function)
(:metaclass mop:funcallable-standard-class))
(defmethod initialize-instance :after ((object funcallable-object) &key function &allow-other-keys)
(setf (funcallable-function object) function))
(demethod (setf funcallable-function) :after (function (object funcallable-object))
(mop:set-funcallable-instance-function object (or function #.(constantly nil))))
(defmethod print-object ((object funcallable-object) stream)
(print-unreadable-object (object stream :type t :identity t)
(with-slots (name) object
(when name
(write name stream)))))
请注意,您必须根据实施方式替换mop
软件包名称,或者使用cl-mop。
编辑:您可以通过以下几种方式使用它。
直接:
(make-instance 'funcallable-object
:name 'foo
:function #'(lambda (<args>)
<body>))
使用宏:
(defmacro nlambda (name (&rest lambda-list) &body body)
`(labels ((,name (,@lambda-list)
,@body))
(make-instance 'funcallable-object
:name ',name
:function #',name)))
(nlambda foo (<args>)
<body>)
我在示例中使用labels
而不是flet
,因为我相信一个名为lambda的更大效用是它的递归能力。
答案 3 :(得分:0)
我刚遇到了pretty-function图书馆,这可能就是您要找的内容。它可以通过quicklisp获得。