2014-12-19 4 views
2

Я хотел бы добавить некоторые метаданные в класс анонимных функций, чтобы их было легко идентифицировать. Пока все мои идеи хакерские или сомнительные:Выполнение анонимных функций легко различимо

Элемент plist будет работать, но, видимо, это только символы, а не лямбды.

я мог вставить что-то в строку документации:

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

Но это, кажется, действительно некрасиво, или:

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

Даже уродливое.

Есть ли не так-плохо-хакерский способ установить атрибут для объекта функции?

Я мог бы также использовать систему типов, чтобы различать свои функции:

Размещение функции в структуре или объект CLOS возможно, но чувствует себя громоздким.

Возможно ли создать лямбда, которые являются обычным подтипом типа функции?

Могут ли объекты CLOS действовать как исполняемые функции? Я думаю о чем-то подобном атрибуту python, вызывающему.

+1

Что касается вашего последнего вопроса, в реализации, которая имеет протокол MOP (метаобъект), вы можете использовать ['funcallable-standard-class'] (http://www.alu.org/mop/dictionary.html) и специализируются на ['print-object'] (http://www.lispworks.com/documentation/HyperSpec/Body/f_pr_obj.htm) для ваших целей. – acelent

ответ

1

EDIT: быстрый и грязный вариант, если ваша реализация печатает имя flet или labels функций:

(defmacro nlambda (name (&rest lambda-list) &body body) 
    `(labels ((,name (,@lambda-list) 
       ,@body)) 
    #',name)) 

Продолжайте читать, если вам нужно надежно печатать имя функции.


Одним из примеров, который следует вашему последнему вопросу, предполагается, что ваш Common Lisp реализации следует протокол метаобъекта (СС):

(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.


EDIT: Вот несколько способов его использования.

Непосредственно:

(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

Я еще не знаю достаточно MOP, чтобы ответить на ваши последние вопросы, но я могу предложить вам хакерский способ сделать то, что вы хотите.

Вы можете объявить специальную переменную для отслеживания lambdas, к которой вы добавили метаданные: (defvar *annotated-lambdas* (make-hash-table)).

Затем определите функцию для получения/установки lambdas из нее.

(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)) 

Конечно, иметь доступ к аннотации, вам нужно чтобы получить доступ к лямбда, который, как я полагаю, вы делаете, потому что иначе вы бы использовали функцию (которая является просто символом, указывающим на лямбда). Также «аннотация» может быть любой, строкой, списком, алистом, plist или даже хэш-таблицей.

+2

Справа. Обычно для этого используются нестандартные слабые хеш-таблицы. Таким образом, функции unreferenced могут получить GCed ... –

+0

@RainerJoswig Не могли бы вы немного рассказать, это звучит интересно. – fstamour

+0

@Sharpie, реализации имеют нестандартную поддержку слабых хеш-таблиц (например, [ABCL] (http://abcl.org/trac/browser/trunk/abcl/src/org/armedbear/lisp/make-hash-table. lisp), [Allegro CL] (http://franz.com/support/documentation/9.0/doc/implementation.htm#cl-make-hash-table-2), [CLisp] (http: //clisp.sourceforge .net/impnotes/weak.html # weak-ht), [Clozure CL] (http://ccl.clozure.com/docs/ccl.html#weak-references), [LispWorks] (http: // www. lispworks.com/documentation/lw61/LW/html/lw-654.htm#pgfId-1039790) и [SBCL] (http://www.sbcl.org/manual/index.html#Hash-Table-Extensions)) чтобы записи были GC'ed. – acelent

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.

+0

Нет никакого способа получить это имя программным путем? Это будет хорошо работать. Это не для печати. – BnMcGn

+0

@BnMcGn: см. Мое редактирование ... –

0

Я только что наткнулся на библиотеку pretty-function, которая может быть тем, что вы ищете. Он доступен через quicklisp.

Смежные вопросы