2009-02-27 33 views
23

Tôi đang cố gắng tạo một hàm trên bay sẽ trả về một giá trị không đổi.Tôi làm cách nào để đóng cửa trong Emacs Lisp?

Trong JavaScript và ngôn ngữ bắt buộc hiện đại khác mà tôi sẽ sử dụng đóng cửa:

function id(a) { 
    return function() {return a;}; 
} 

nhưng Emacs lisp không hỗ trợ những người.

Tôi có thể tạo kết hợp chức năng nhận dạng và ứng dụng chức năng một phần nhưng cũng không được hỗ trợ.

Vậy làm cách nào để thực hiện điều đó?

+0

Theo như tôi đã nghe, JavaScript thực sự khá chức năng. – Svante

+0

Nó phụ thuộc vào quan điểm của một người. Đối với tôi, nếu hầu hết các mã trong ngôn ngữ là bắt buộc thì nó bắt buộc. Đó là trường hợp ở đây. – vava

+0

Kể từ phiên bản 24, Emacs hiện có phạm vi từ vựng. –

Trả lời

8

ý tưởng ngu ngốc: làm thế nào về:

(defun foo (x) 
    `(lambda() ,x)) 

(funcall (foo 10)) ;; => 10 
+1

Điều này bị hỏng khi bạn muốn viết một cái gì đó như: (lexical-let ((a 0)) (khuyết điểm (lambda() a) (lambda (mới-a) (setf a new-a)))) – jrockway

28

Tìm thấy một giải pháp với từ vựng-hãy

(defun foo (n) 
    (lexical-let ((n n)) #'(lambda() n))) 

(funcall (foo 10)) ;; => 10 
6

Tôi không vững chắc trong Emacs Lisp, nhưng như xa như tôi biết, sự khác biệt lớn từ Lisp phổ biến là nó sử dụng phạm vi năng động trong suốt. Các trạng thái Emacs Lisp Manual rằng Emacs Lisp không có bao đóng.

Tôi sẽ cố gắng áp dụng kiến ​​thức lý thuyết của mình về phạm vi động.

Nếu bạn có một hàm id mà chỉ trả về giá trị của my-id:

 
(defun id() 
    my-id) 

và bạn sử dụng nó trong một số chức năng khác:

 
(defun some-other-place() 
    (id)) 

và ở đâu đó trên đường đi theo tiếng gọi của id bạn ràng buộc my-id qua ví dụ một phép:

 
(defun even-elsewhere() 
    (let ((my-id 5)) 
    (some-other-place))) 

này nên trả lại 5.

Tôi biết rằng Phạm vi năng động là một con thú kỳ lạ khi bạn đang sử dụng để xác định phạm vi từ vựng, nhưng có lẽ bạn có thể sử dụng để thực hiện hành vi mong muốn của bạn.

+0

Wow , điều đó thật thú vị :) – vava

7

Emacs lisp chỉ có phạm vi động. Có một macro lexical-let xấp xỉ phạm vi từ vựng thông qua một bản hack khá khủng khiếp.

+9

Tất nhiên, 'hack khá khủng khiếp' là những gì diễn ra dưới sự che chở của các triển khai ngôn ngữ khác. – jrockway

4

Emacs 24 có liên kết từ vựng.

http://www.emacswiki.org/emacs/LexicalBinding

+0

Tham khảo phần trong sách hướng dẫn của GNU Emacs về [Phạm vi biến đổi] (https://www.gnu.org/software/emacs/manual/html_node/elisp/Variable-Scoping.html#Variable-Scoping) – dat

12

Real (Không Fake) đóng cửa trong Emacs 24.

Mặc dù Emacs 24 có tát từ vựng khi biến từ vựng-binding có giá trị t, các defun đặc biệt hình thức không hoạt động đúng trong bối cảnh ràng buộc từ vựng (ít nhất là không phải trong Emacs 24.2.1.) Điều này làm cho nó khó khăn, nhưng không không thể, để xác định thực (không giả) đóng cửa.Ví dụ:

(let ((counter 0)) 
    (defun counting() 
    (setq counter (1+ counter)))) 

sẽ không hoạt động như mong đợi vì các biểu tượng truy cập trong defun sẽ bị ràng buộc vào biến toàn cầu của tên đó, nếu có một, và không phải là biến từ vựng xác định trong để. Khi hàm đếm được gọi, nếu biến toàn cục không tồn tại thì sẽ rõ ràng là không thành công. Hoever nếu có một biến toàn cầu như vậy nó được cập nhật, mà có lẽ không phải là những gì đã được dự định và có thể là một khó khăn để theo dõi lỗi kể từ khi chức năng có vẻ như đang hoạt động đúng.

biên dịch

Byte không đưa ra một cảnh báo nếu bạn sử dụng defun theo cách này và có lẽ vấn đề này sẽ được giải quyết trong một số phiên bản tương lai của Emacs, nhưng cho đến khi đó macro sau có thể được sử dụng:

(defmacro defun** (name args &rest body) 
    "Define NAME as a function in a lexically bound context. 

Like normal `defun', except that it works correctly in lexically 
bound contexts. 

\(fn NAME ARGLIST [DOCSTRING] BODY...)" 
    (let ((bound-as-var (boundp `,name))) 
    (when (fboundp `,name) 
     (message "Redefining function/macro: %s" `,name)) 
    (append 
    `(progn 
     (defvar ,name nil) 
     (fset (quote ,name) (lambda (,@args) ,@body))) 
    (if bound-as-var 
     'nil 
     `((makunbound `,name)))))) 

Nếu bạn xác định đếm như sau:

(let ((counter 0)) 
    (defun** counting() 
    (setq counter (1+ counter)))) 

nó sẽ làm việc như mong đợi và cập nhật các lexically ràng buộc biến đếm mỗi khi được gọi, trong khi trả về giá trị mới.

caveat: vĩ mô sẽ không hoạt động đúng nếu bạn cố gắng defun ** một hàm có cùng tên là một trong những biến lexically ràng buộc. I.e nếu bạn làm điều gì đó như:

(let ((dont-do-this 10)) 
    (defun** dont-do-this() 
    ......... 
    .........)) 

Tôi không thể tưởng tượng bất kỳ ai thực sự làm điều đó nhưng nó đáng được đề cập đến.

Lưu ý: Tôi đã đặt tên cho vĩ mô defun **để nó không xung đột với các vĩ mô defun * trong cl gói, tuy nhiên nó không phụ thuộc vào bất kỳ cách nào vào đó gói.

+0

Bạn có thể đưa ra một ví dụ hoàn chỉnh? Dường như mã (let ((counter 0)) (defun ** counting() (bộ đếm setq (1+ counter)))) không hoạt động như mong đợi. – toolchainX

+1

Các giới hạn wrt để xác định đóng cửa với 'defun' đã được nâng lên trong Emacs-24.3 (nơi' defun' bây giờ được định nghĩa là một vĩ mô chứ không phải là một hình thức đặc biệt). Vì vậy, kể từ 24,3 'defun' hoạt động giống như' defun ** 'macro của bạn (tho mà không bị hỏng' (defvar, name nil) 'và sửa các nhược điểm nhỏ khác như sử dụng' fset' thay vì 'defalias' và xử lý được gọi là "docstrings động" mà yêu cầu thay đổi trong bytecompiler). – Stefan

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