Как закрытие может относиться к себе? - PullRequest
5 голосов
/ 08 февраля 2012

Предположим, у меня есть замыкание с садовым разнообразием, как в этом примере с обнаженными костями:

(let ((alpha 0) #| etc. |# )
  (lambda ()
    (incf alpha)
    #| more code here |#
    alpha))

Предположим, я (funcall) трижды закрывал экземпляр этого замыкания, и в середине третьего выполнения этоЗакрытие хочет сохранить себя где-то (скажем, в хэш-таблице).Тогда я не (funcall) этот экземпляр на некоторое время.Затем я извлекаю этот экземпляр из хеш-таблицы и снова (funcall), получая возвращаемое значение 4.

Как функция в замыкании ссылается на себя, чтобы она могла сохранить себя в этой хеш-таблице?

РЕДАКТИРОВАТЬ 1: Вот более подробный пример.Я достигаю цели, передавая закрытие себе в качестве параметра.Но я бы хотел, чтобы замыкание делало все это для себя без самопараметризации.

 1 (defparameter *listeriosis* nil)
 2 (defparameter *a*
 3   (lambda ()
 4     (let ((count 0))
 5       (lambda (param1 param2 param3 self)
 6         (incf count)
 7         (when (= 3 count)
 8           (push self *listeriosis*)
 9           (push self *listeriosis*)
10           (push self *listeriosis*))
11         count))))
12 (let ((bee (funcall *a*)))
13   (princ (funcall bee 1 2 3 bee)) (terpri)
14   (princ (funcall bee 1 2 3 bee)) (terpri)
15   (princ (funcall bee 1 2 3 bee)) (terpri)
16   (princ (funcall bee 1 2 3 bee)) (terpri)
17   (princ (funcall bee 1 2 3 bee)) (terpri))
18 (princ "///") (terpri)
19 (princ (funcall (pop *listeriosis*) 1 2 3 nil)) (terpri)
20 (princ (funcall (pop *listeriosis*) 1 2 3 nil)) (terpri)
21 (princ (funcall (pop *listeriosis*) 1 2 3 nil)) (terpri)
1
2
3
4
5
///
6
7
8

РЕДАКТИРОВАТЬ 2: Да, я знаю, что могу использовать макрос, чтобы вставить имя функции в качестве еепервый параметр, а затем использовать этот макрос вместо (funcall), но я все же хотел бы знать, как позволить закрытию ссылаться на его собственный экземпляр.

РЕДАКТИРОВАТЬ 3: В ответ на любезное предложение SK-logicЯ сделал следующее, но он не делает то, что я хочу.Он помещает три новых замыкания в стек, а не три ссылки на одно и то же замыкание.Посмотрите, как, когда я вытаскиваю их из стека, значения вызовов равны 1, 1 и 1 вместо 6, 7 и 8?

 1 (defparameter *listeriosis* nil)
 2 (defun Y (f) 
 3   ((lambda (x) (funcall x x)) 
 4    (lambda (y) 
 5      (funcall f (lambda (&rest args) 
 6                (apply (funcall y y) args))))))
 7 (defparameter *a*
 8   (lambda (self)
 9     (let ((count 0))
10       (lambda (param1 param2 param3)
11         (incf count)
12         (when (= 3 count)
13           (push self *listeriosis*)
14           (push self *listeriosis*)
15           (push self *listeriosis*))
16         count))))
17 (let ((bee (Y *a*)))
18   (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
19   (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
20   (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
21   (princ (funcall bee 1 2 3 #| bee |# )) (terpri)
22   (princ (funcall bee 1 2 3 #| bee |# )) (terpri))
23 (princ "///") (terpri)
24 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
25 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
26 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
1
2
3
4
5
///
1
1
1

РЕДАКТИРОВАТЬ 4: Предложение Джона О точно достигло цели,Вот код и вывод:

 1 (defparameter *listeriosis* nil)
 2 (defparameter *a*
 3   (lambda ()
 4     (let ((count 0))
 5       (labels ((self (param1 param2 param3)
 6                  (incf count)
 7                  (when (= 3 count)
 8                    (push (function self) *listeriosis*)
 9                    (push (function self) *listeriosis*)
10                    (push (function self) *listeriosis*))
11                  count))
12         (function self)))))
13 (let ((bee (funcall *a*)))
14   (princ (funcall bee 1 2 3)) (terpri)
15   (princ (funcall bee 1 2 3)) (terpri)
16   (princ (funcall bee 1 2 3)) (terpri)
17   (princ (funcall bee 1 2 3)) (terpri)
18   (princ (funcall bee 1 2 3)) (terpri))
19 (princ "///") (terpri)
20 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
21 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
22 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
1
2
3
4
5
///
6
7
8

РЕДАКТИРОВАТЬ 5. Предложение Мирон также попадает в цель и фактически делает код немного более читабельным:

 1 (defmacro alambda (parms &body body)
 2   `(labels ((self ,parms ,@body))
 3     #'self))
 4 ;
 5 (defparameter *listeriosis* nil)
 6 (defparameter *a*
 7   (lambda ()
 8     (let ((count 0))
 9       (alambda (param1 param2 param3)
10         (incf count)
11         (when (= 3 count)
12           (push #'self *listeriosis*)
13           (push #'self *listeriosis*)
14           (push #'self *listeriosis*))
15         count))))
16 ;
17 (let ((bee (funcall *a*)))
18   (princ (funcall bee 1 2 3)) (terpri)
19   (princ (funcall bee 1 2 3)) (terpri)
20   (princ (funcall bee 1 2 3)) (terpri)
21   (princ (funcall bee 1 2 3)) (terpri)
22   (princ (funcall bee 1 2 3)) (terpri))
23 (princ "///") (terpri)
24 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
25 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
26 (princ (funcall (pop *listeriosis*) 1 2 3)) (terpri)
1
2
3
4
5
///
6
7
8

Ответы [ 2 ]

7 голосов
/ 08 февраля 2012

Я не думаю, что вам нужно идти так далеко, чтобы определить Y-комбинатор для себя; встроенная форма labels создаст необходимые вам ссылки. Согласно HyperSpec :

"labels эквивалентно flet, за исключением того, что область имен определенных функций для меток охватывает как сами определения функций, так и тело."

Вот любимый всеми пример закрытия игрушек, показывающий, как локально определенный f закрывается над собственной привязкой:

(defun make-counter (n)
   (labels ((f () (values (incf n) (function f))))
     (function f)))

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

CL-USER> (setq g (make-counter 5))
#<FUNCTION F NIL (BLOCK F (VALUES (INCF N) #'F))>
CL-USER> (multiple-value-bind (n q) (funcall g) (list n (funcall q)))
(6 7)

Должно быть проще распространить это на сохранение замыкания в структуре данных вместо его возврата.

4 голосов
/ 08 февраля 2012

А как насчет alambda (также в On Lisp)?

;; Graham's alambda
(defmacro alambda (parms &body body)
  `(labels ((self ,parms ,@body))
     #'self))
...