2012-05-20 14 views
11

Sau khi đọc rất nhiều tài liệu liên quan đến nhà điều hành Lisp eval-when Tôi vẫn không thể hiểu được cách sử dụng của nó, tôi biết với nhà điều hành này tôi có thể kiểm soát thời gian đánh giá biểu thức của mình nhưng tôi không thể tìm ra ví dụ nào.Đánh giá khi nào sử dụng?

Trân trọng, utxeee.

Trả lời

21

Compilation của một tập tin Lisp

Đưa ví dụ như biên soạn một file Lisp. Trình biên dịch Lisp xử lý các biểu mẫu mức cao nhất. Đây có thể là hình thức tùy ý Lisp, DEFUNs, DEFMACROS, DEFCLASS, gọi hàm, ...

Toàn bộ câu chuyện như thế nào trình biên dịch tập tin làm việc là quá phức tạp để giải thích ở đây, nhưng một vài điều:

  • trình biên dịch tệp tạo mã cho biểu mẫu (DEFUN foo()). Nhưng nó không thực hiện các hình thức defun. Vì vậy, trong quá trình biên dịch, nó được biết rằng có một hàm FOO, nhưng mã của ˋFOOˋ không có sẵn trong quá trình biên dịch. Trình biên dịch tạo mã cho tệp được biên dịch, nhưng không giữ nó trong bộ nhớ. Bạn không thể gọi một hàm như vậy tại thời gian biên dịch.

  • cho macro hoạt động hơi khác: (DEFMACRO BAZ ...). Trình biên dịch tệp sẽ không chỉ biên dịch macro và lưu ý rằng nó có ở đó, nhưng nó cũng sẽ làm cho macro sẵn có tại thời gian biên dịch. Nó được nạp vào môi trường biên dịch .

Như vậy tưởng tượng chuỗi các hình thức trong một tập tin:

(defmacro baz ...) 

(defun foo() (baz ...)) 

này hoạt động bởi vì trình biên dịch tập tin biết vĩ mô BAZ và khi nó biên dịch mã cho FOO, sau đó nó có thể mở rộng các hình thức vĩ mô .

Bây giờ chúng ta hãy nhìn vào ví dụ sau:

(defun bar (form) ...) 

(defmacro baz (form) (bar form)) 

(defun foo() (baz ...)) 

Trên sẽ không hoạt động. Bây giờ macro BAZ sử dụng hàm BAR bằng cách gọi hàm đó. Khi trình biên dịch cố gắng biên dịch hàm FOO, nó không thể mở rộng macro BAZ vì không thể gọi BAR vì mã BAR không được tải vào môi trường biên dịch.

Có hai giải pháp này:

  1. biên dịch tải BAR trước đó sử dụng một tập tin riêng biệt.
  2. Sử dụng EVAL-WHEN

Ví dụ cho EVAL-WHEN:

(eval-when (:compile-toplevel :execute :load-toplevel) 
    (defun bar (form) ...) 
) 

(defmacro baz (form) (bar form)) 

(defun foo() (baz ...)) 

Bây giờ EVAL-WHEN chỉ thị biên dịch tập tin để thực sự chạy dưới dạng defun quá trình biên dịch.Hiệu quả của việc này là: trình biên dịch tệp giờ đã biết định nghĩa của BAR tại thời gian biên dịch. Vì vậy, nó có sẵn sau này, khi trình biên dịch tập tin cần gọi BAR trong khi mở rộng macro của việc sử dụng BAZ.

Bạn chỉ có thể sử dụng :compile-toplevel, khi không cần chức năng sau khi biên soạn tệp. Nếu nó được sử dụng sau, thì chúng ta cần phải chắc chắn rằng nó được nạp.

Vì vậy EVAL-WHEN cho phép để xác định nếu một đoạn cụ thể của mã nên được chạy

  • quá trình biên dịch của một tập tin
  • trong quá trình tải về một tập tin
  • trong quá trình thực

EVAL-WHEN là không được sử dụng thường xuyên trong mã người dùng. Nếu bạn sử dụng nó, sau đó bạn nên tự hỏi mình nếu bạn thực sự cần nó.

+0

Hi Rainer, nhưng tại sao chúng ta cần phải sử dụng cờ: thực thi và: tải-top-level trong việc sử dụng eval-khi nào? – utxeee

+0

Cảm ơn Rainer lần nữa vì câu trả lời của bạn đã chỉnh sửa câu trả lời trước: D Nhưng tôi vẫn còn nghi ngờ, tại sao tôi cần sử dụng các cờ: load-top-level và: execute. Từ văn bản của bạn, tôi có thể thấy rằng chúng ta nên sử dụng cờ: tải-top-level nếu chức năng được sử dụng sau này. Vậy tại sao chúng ta không cần sử dụng eval-when với: load-top-level trong tất cả các hàm khác sẽ được sử dụng sau này? – utxeee

+0

@utxeee: tải-top-level là mặc định trong quá trình biên dịch, khi không có EVAL-WHEN. –