使匿名功能易于区分

时间:2014-12-19 02:38:37

标签: lambda common-lisp

我想在一类匿名函数中添加一些元数据,以便于识别。到目前为止,我的所有想法都是黑客或可疑的:

一个plist项目可以工作,但显然它只适用于符号,而不是lambdas。

我可以在docstring中插入一些内容:

(lambda (x) "is-special" ...

但这看起来很难看,或者:

(lambda (x)
  (if 
   (eq x 'special) 
   t
   ...

甚至更丑。

在函数对象上设置属性是否有一种不那么糟糕的hackish方式?

我也可以使用类型系统来区分我的功能:

可以将函数放在结构或CLOS对象中,但感觉很麻烦。

是否可以创建作为函数类型的自定义子类型的lambda?

可以使CLOS对象充当可执行函数吗?我正在考虑类似于python的调用属性。

4 个答案:

答案 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)

编辑:一个快速而肮脏的选项,如果您的实现打印出fletlabels函数的名称:

(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获得。