2016-05-05 28 views

Trả lời

9

Hãy nói rằng chúng ta có đoạn mã sau:

(defmacro inner-macro [arg] 
    `(println ~arg)) 

(defmacro top-level-macro [arg] 
    `(inner-macro ~arg)) 

(defn not-a-macro [] nil) 

Sau đó, doc của macroexpand-1 nói:

Nếu hình thức đại diện cho một hình thức vĩ mô, trả lại sự bành trướng của nó, khác trả về hình thức.

Trên thực tế, nó:

user> (macroexpand-1 '(inner-macro "hello")) 
(clojure.core/println "hello") 

user> (macroexpand-1 '(top-level-macro "hello")) 
(user/inner-macro "hello") 

user> (macroexpand-1 '(not-a-macro)) 
(not-a-macro) 

Nói cách khác, macroexpand-1 không chỉ là một bước của macroexpansion nếu hình thức cung cấp là một hình thức vĩ mô.

Sau đó, doc của macroexpand:

Liên tiếp gọi macroexpand-1 vào mẫu cho đến khi nó không còn đại diện cho một hình thức vĩ mô, sau đó trả về nó.

Ví dụ:

user> (macroexpand '(top-level-macro "hello")) 
(clojure.core/println "hello") 

gì đã xảy ra? Ngay sau khi, (top-level-macro "hello") mở rộng thành (user/inner-macro "hello"), dưới dạng macro, macroexpand sẽ thực hiện mở rộng lại một lần nữa. Kết quả của việc mở rộng thứ hai là (clojure.core/println "hello"). Nó không phải là một hình thức vĩ mô, vì vậy macroexpand chỉ trả về nó.

Vì vậy, chỉ để diễn đạt lại doc, macroexpand sẽ đệ quy làm mở rộng cho đến khi cấp cao nhất hình thức không phải là một hình thức vĩ mô.

Cũng có thêm lưu ý trong doc macroexpand 's:

Lưu ý không macroexpand-1 cũng không macroexpand mở rộng macro trong subforms.

Điều đó có nghĩa là gì? Hãy nói rằng chúng tôi có một vĩ mô hơn:

(defmacro subform-macro [arg] 
    `(do 
    (inner-macro ~arg))) 

Hãy thử để mở rộng nó:

user> (macroexpand-1 '(subform-macro "hello")) 
(do (user/inner-macro "hello")) 

user> (macroexpand '(subform-macro "hello")) 
(do (user/inner-macro "hello")) 

Từ, (do ...) hình thức không phải là một vĩ mô macroexpand-1macroexpand chỉ trả lại và không có gì hơn. Đừng nghĩ rằng macroexpand sẽ làm như sau:

user> (macroexpand '(subform-macro "hello")) 
(do (clojure.core/println "hello")) 
4

sự khác biệt này khá đơn giản. Đầu tiên của tất cả các nền: khi trình biên dịch nhìn thấy cuộc gọi macro nó cố gắng mở rộng nó theo định nghĩa của nó.Nếu mã, được tạo bởi macro này chứa các macro khác, chúng cũng được mở rộng bởi trình biên dịch, và cứ thế, cho đến khi mã kết quả hoàn toàn không có macro. Vì vậy, macroexpand-1 chỉ mở rộng macro trên cùng và hiển thị kết quả (không có vấn đề gì tạo ra một cuộc gọi macro khác), trong khi macroexpand cố gắng làm theo đường dẫn của trình biên dịch (một phần, không mở rộng macro trong biểu mẫu con). clojure.walk/maxroexpand-all).

nhỏ ví dụ:

user> (defmacro dummy [& body] 
     `(-> [email protected])) 
#'user/dummy 

vĩ mô ngớ ngẩn này tạo ra các cuộc gọi đến một vĩ mô (->)

user> (macroexpand-1 '(dummy 1 (+ 1))) 
(clojure.core/-> 1 (+ 1)) 

macroexpand-1 chỉ mở rộng dummy, nhưng giữ -> chưa giãn nở

user> (macroexpand '(dummy 1 (+ 1))) 
(+ 1 1) 

macroexpand mở rộng dummy và sau đó mở rộng ->

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