2012-07-21 43 views
9

Trong nhiệm vụ của tôi để hiểu đầy đủ các macro lisp mạnh mẽ như vậy, một câu hỏi xuất hiện trong đầu tôi. Tôi biết rằng quy tắc vàng về macro là một quy tắc "Không bao giờ sử dụng macro khi chức năng sẽ thực hiện công việc". Tuy nhiên đọc Chương 9 - Practical: Building a Unit Test Framework - từ cuốn sách Thực hành chung Lisp Tôi đã được giới thiệu với macro dưới đây có mục đích là để loại bỏ sự trùng lặp của biểu thức trường hợp thử nghiệm, với nguy cơ bị gắn nhãn sai kết quả.Lisp: Macros vs Chức năng

;; Function defintion. 

(defun report-result (result form) 
    (format t "~:[FAIL~;pass~] ... ~a~%" result form)) 

;; Macro Definition 

(defmacro check (form) 
    `(report-result ,form ',form)) 

OK, tôi hiểu mục đích của nó nhưng tôi có thể thực hiện nó bằng cách sử dụng chức năng thay vì một vĩ mô, ví dụ:

(setf unevaluated.form '(= 2 (+ 2 3))) 

(defun my-func (unevaluated.form) 
    (report-result (eval unevaluated.form) unevaluated.form)) 
  1. là chỉ này có thể bởi vì vĩ mô nhất định là quá đơn giản ?
  2. Hơn nữa, Hệ thống Macro Lisp có mạnh mẽ như vậy đối thủ tương đối do mã của nó - giống như cấu trúc điều khiển, chức năng, vv - được biểu diễn như một DANH SÁCH?
+6

"Quy tắc vàng" này là ngu ngốc. Sử dụng macro bất cứ nơi nào bạn tìm thấy chúng phù hợp và quên đi tất cả các "quy tắc" não bị hư hại. Đối với ví dụ của bạn, nó không phải là gần như một tương đương, kể từ khi bạn đang trì hoãn một trình biên dịch để chạy. Nếu form của bạn chứa các tham chiếu đến một số tên scoped cục bộ thì nó sẽ không hoạt động. –

+0

Sk, bạn có thể đưa ra một ví dụ về hàm eval và tên phạm vi cục bộ không? – utxeee

+3

'(let ((x 2)) (eval '(+ x x)))' đơn giản sẽ không hoạt động, OTOH nếu biểu mẫu '(+ x x)' này được tạo ra bởi một macro nó sẽ được biên dịch tự nhiên. –

Trả lời

4

Việc thay thế tốt hơn là không phải với eval, mà sẽ không thực hiện như mong đợi cho mọi trường hợp (ví dụ, nó không có quyền truy cập vào môi trường từ vựng), và cũng có thể là quá mức cần thiết (xem ở đây: https://stackoverflow.com/a/2571549/977052), nhưng một cái gì đó sử dụng chức năng mang tính chất như thế này:

(defun check (fn) 
    (report-result (funcall fn) (function-body fn))) 

CL-USER> (check (lambda() (= 2 (+ 2 3)))) 

Bằng cách này, đây là cách những điều đó đều thực hiện trong Ruby (chức năng ẩn danh được gọi là procs có). Tuy nhiên, như bạn thấy, nó sẽ trở nên kém thanh lịch hơn (trừ khi bạn thêm đường cú pháp) và thực sự là một vấn đề lớn hơn: không có hàm function-body trong Lisp (mặc dù có thể có cách không chuẩn). . Nhìn chung, như bạn thấy, đối với nhiệm vụ cụ thể này, các giải pháp thay thế là tồi tệ hơn đáng kể, mặc dù trong một số trường hợp, cách tiếp cận này có thể hoạt động. Tuy nhiên, nói chung, nếu bạn muốn làm điều gì đó với mã nguồn của các biểu thức được chuyển vào macro (và thường thì đây là lý do chính của việc sử dụng macro), thì các hàm sẽ không đủ.

12

Nhưng nếu nó là một vĩ mô bạn, có thể làm:

(check (= 2 (+ 2 3))) 

Với một hàm, bạn phải làm:

(check '(= 2 (+ 2 3))) 

Ngoài ra, với các vĩ mô (= 2 (+ 2 3)) là thực sự biên soạn bởi trình biên dịch, trong khi với hàm được đánh giá bởi hàm eval, không nhất thiết phải giống nhau.

Addenda:

Có, nó chỉ đánh giá chức năng. Điều đó có nghĩa là phụ thuộc vào việc thực hiện. Một số có thể giải thích nó, những người khác có thể biên dịch và thực hiện nó. Nhưng vấn đề đơn giản là bạn không biết từ hệ thống đến hệ thống.

Môi trường từ vựng rỗng mà những người khác đang đề cập cũng là một vấn đề lớn.

xem xét:

(defun add3f (form) 
    (eval `(+ 3 ,form))) 

(demacro add3m (form) 
    `(+ 3 ,form)) 

Sau đó quan sát:

[28]> (add3m (+ 2 3)) 
8 
[29]> (add3f '(+ 2 3)) 
8 
[30]> (let ((x 2)) (add3m (+ x 3))) 
8 
[31]> (let ((x 2)) (add3f '(+ x 3))) 

*** - EVAL: variable X has no value 
The following restarts are available: 
USE-VALUE  :R1  Input a value to be used instead of X. 
STORE-VALUE :R2  Input a new value for X. 
ABORT   :R3  Abort main loop 
Break 1 [32]> :a 

Đó là thực sự khá nguyền rủa đối với hầu hết các trường hợp sử dụng. Vì eval không có môi trường từ vựng, nó không thể "nhìn thấy" x từ kèm theo let.

+0

Sẽ, vì vậy sử dụng ở đây eval chức năng Tôi chỉ đánh giá biểu thức? – utxeee

+1

@uxtee: Theo mặc định, 'eval' hoạt động trong môi trường từ vựng rỗng. Đó là gần như không bao giờ những gì bạn muốn. – Vatine

3

Hàm report-result cần cả mã nguồn và kết quả thực thi.

Macro CHECK cung cấp cả hai từ một biểu mẫu nguồn duy nhất.

Nếu bạn đặt một loạt các hình thức check vào tệp, chúng có thể dễ dàng được biên dịch bằng cách sử dụng quy trình biên dịch các tệp Lisp thông thường. Bạn sẽ nhận được một phiên bản biên dịch mã kiểm tra.

Sử dụng hàm và EVAL (sử dụng tốt hơn COMPILE) bạn sẽ trì hoãn đánh giá nguồn sau này. Nó cũng sẽ không được rõ ràng nếu nó được giải thích hoặc biên soạn. Trong trường hợp biên dịch, sau đó bạn sẽ nhận được kiểm tra của trình biên dịch.

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