2012-02-08 45 views
5

Giả sử tôi có một đóng cửa vườn đa dạng như xương trần mẫu này:Làm cách nào để đóng cửa tự giới thiệu?

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

Giả sử tôi (funcall) một ví dụ về điều đó đóng cửa ba lần, và ở giữa việc thực hiện thứ ba, việc đóng cửa này muốn cứu bản thân một nơi nào đó (trong một bảng băm, nói). Sau đó, tôi không (funcall) trường hợp này trong một thời gian. Sau đó, tôi lấy trường hợp này từ bảng băm và (funcall) một lần nữa, nhận giá trị trả lại là 4.

Chức năng trong bao đóng tham chiếu đến chính nó, để nó có thể tự lưu trong bảng băm đó?

EDIT 1: Dưới đây là ví dụ chi tiết hơn. Tôi hoàn thành mục tiêu bằng cách vượt qua việc đóng cửa cho chính nó như là một tham số. Nhưng tôi muốn đóng cửa tự mình làm tất cả những điều này mà không bị tự tham số hóa.

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 

EDIT 2: Vâng, tôi biết tôi có thể sử dụng một macro để trượt tên của các chức năng trong như tham số đầu tiên của nó, và sau đó sử dụng macro rằng thay vì (funcall), nhưng tôi vẫn muốn biết làm thế nào để cho một đóng cửa đề cập đến trường hợp riêng của nó.

EDIT 3: Để trả lời đề xuất loại SK-logic, tôi đã làm như sau, nhưng nó không làm những gì tôi muốn. Nó đẩy ba đóng mới vào ngăn xếp, không phải ba tham chiếu đến cùng một đóng. Xem cách khi tôi bật những người đó ra khỏi ngăn xếp, giá trị của các cuộc gọi là 1, 1 và 1 thay vì 6, 7 và 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 

CHỈNH SỬA 4: Đề xuất của Jon O đánh dấu chính xác. Dưới đây là các mã và đầu ra:

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 

EDIT 5: đề nghị Miron cũng đánh dấu, và thực sự làm cho mã một chút dễ đọc hơn:

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 
+0

Tôi giả định bằng cách "đóng" nghĩa là "chức năng ẩn danh". Trong mọi trường hợp, tôi không hiểu tại sao bạn không chỉ đơn giản là đặt tên cho nó? – delnan

+0

Tôi không muốn một trường hợp mới đóng cửa. Tôi muốn cái cũ, với những thay đổi liên tục đến các biến kèm theo. Trong thực tế, có thể có một số trường hợp, mỗi trường hợp nên được muối đi đâu đó. Tất cả điều này có thể được thực hiện khi đặt tên cho nó không? Cú pháp sẽ là gì? –

+0

Tôi không nói lisp rất tốt, nhưng tôi cho rằng một cái gì đó giống như "biểu thức chức năng tự thực hiện" phổ biến trong JavaScript sẽ làm các trick. – delnan

Trả lời

4

Điều gì về alambda (cũng trong On Lisp)?

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

Tôi đã sử dụng điều này trong EDIT 5 của câu hỏi. Nó thực sự làm cho mã dễ đọc hơn. Cảm ơn! –

+2

Vâng, đó là triển khai chung nhất cho ý tưởng 'nhãn'. Bạn cũng có thể tìm thấy 'blambda' hữu ích, nơi bạn cũng có thể đặt tên là' self'. Xem [Alexandria] (http://common-lisp.net/project/alexandria/draft/alexandria.html) để biết thêm ví dụ về điều này (tìm kiếm 'if-let'). –

+2

Ví dụ 'blambda':' (defmacro blambda (fn-name args & body body) \ '(nhãn ((, fn-name, args, @ body)) # ', fn-name))', được sử dụng trong ' (funcall (blambda fact (x) (if (= 0 x) 1 (* x (thực tế (1- x))))) 5) ' –

7

Tôi không nghĩ rằng bạn cần phải đi như xa như xác định tổ hợp Y cho chính mình để làm điều này; được xây dựng ở dạng labels sẽ tạo ra các ràng buộc tự tham chiếu mà bạn cần. Theo số HyperSpec:

"labels tương đương với flet ngoại trừ phạm vi của tên hàm được xác định cho nhãn bao gồm cả định nghĩa chức năng cũng như nội dung."

Dưới đây là yêu thích ví dụ đồ chơi đóng cửa của mọi người, thể hiện như thế nào tại địa phương xác định f đóng trên riêng của mình ràng buộc:

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

này trả về một kết thúc mà trả về hai giá trị: giá trị mới của bộ đếm, và riêng của mình giá trị hàm. Ví dụ sử dụng:

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) 

Cần đơn giản để mở rộng để lưu trữ kết cấu trong cấu trúc dữ liệu thay vì trả lại.

+0

Hoàn hảo! (Xem kết quả trong câu hỏi đã chỉnh sửa.) Cảm ơn bạn! –

+0

Rất vui vì nó hữu ích! –

+1

@Thanh toán: Có liên quan (nhưng không trùng lặp): http://stackoverflow.com/q/7936024/13, điều này giải thích tại sao các trình kết hợp 'nhãn' hoặc' letrec' hoặc Y hoặc tương tự là bắt buộc. (Tiết lộ: Tôi đã viết câu trả lời được chấp nhận.) –

Các vấn đề liên quan