Xây dựng danh sách rõ ràng là "đơn giản nhất", theo cách nào đó, vì có vài khái niệm cốt lõi bạn cần biết: chỉ chấp nhận danh sách và thay đổi nó cho đến khi bạn có danh sách mới. Backtick là một phím tắt thuận tiện cho các khối "templating"; có thể viết bất kỳ macro nào mà không có nó, nhưng đối với bất kỳ macro lớn nào thì nó nhanh chóng trở nên rất khó chịu. Ví dụ, hãy xem xét hai cách viết let
như một vĩ mô đối với fn
:
(defmacro let [bindings & body]
(let [names (take-nth 2 bindings)
vals (take-nth 2 (rest bindings))]
`((fn [[email protected]]
(do [email protected]))
[email protected])))
(defmacro let [bindings & body]
(let [names (take-nth 2 bindings)
vals (take-nth 2 (rest bindings))]
(cons (list `fn (vec names) (cons `do body))
vals)))
Trong trường hợp đầu tiên, sử dụng backtick làm cho nó khá rõ ràng rằng bạn đang viết một hàm trong những cái tên có chứa cơ thể, và sau đó gọi nó với các giá trị - mã macro được "định hình" giống như mã mở rộng, vì vậy bạn có thể tưởng tượng nó sẽ trông như thế nào.
Trong trường hợp thứ hai, chỉ với cons
và list
tất cả các nơi, đó là một cơn đau đầu thực sự để làm việc ra những gì sẽ mở rộng như thế nào. Điều này không phải luôn luôn như vậy, tất nhiên: đôi khi nó có thể rõ ràng hơn để viết một cái gì đó mà không có một backtick.
Một điểm rất quan trọng khác được thực hiện bởi Kyle Burton: print
không giống như 'print
! Việc mở rộng macro của bạn phải chứa biểu tượng print
, không phải giá trị của nó (là một hàm). Các đối tượng nhúng (chẳng hạn như các hàm) trong mã rất dễ vỡ và chỉ hoạt động một cách tình cờ. Vì vậy, hãy đảm bảo macro của bạn mở rộng sang mã bạn thực sự có thể tự viết và để hệ thống đánh giá thực hiện công việc khó khăn - bạn có thể nhập vào biểu tượng print
, nhưng bạn không thể nhập con trỏ vào giá trị hiện tại của hàm print
.
Nó thực sự tương đương với '(danh sách \ 'in a)', hơi khác: '' in' sẽ chỉ hoạt động nếu người gọi có' print' tham chiếu đến 'clojure.core/print' chứ không phải là cục bộ, hoặc không có ràng buộc nào cả (nói rằng họ đã loại trừ nó khỏi ': refer-clojure' của không gian tên của chúng). '\' print', mặt khác, mở rộng trực tiếp đến 'clojure.core/print' để nó không rõ ràng và chính xác trong bất kỳ ngữ cảnh nào. – amalloy
@amalloy Cảm ơn bạn đã trả lời, dựa trên sugestion của bạn, tôi đã thử thực hiện macroexpand (thông qua slime) trên '' '(print ~ a)' và tôi đã trở lại: '(seq (concat (list 'print) (liệt kê a))) '- ah, nhưng bạn đang nói rằng dạng tiền được biến đổi là giống như việc có' '' in' phải không? –
Tôi không thể đọc phản hồi của bạn rất tốt vì SO đang ăn một số ký tự backtick, nhưng: Slime đang ẩn các không gian tên từ bạn (và macroexpand là không cần thiết). Nếu bạn chỉ trích dẫn biểu thức, bằng cách gõ ''\' (print ~ a) 'trong repl, bạn sẽ thấy nó tương đương với' (clojure.core/seq (clojure.core/concat (clojure.core/list (quote) clojure.core/print)) (clojure.core/list a))) '. 'Clojure.core/print' là sự khác biệt quan trọng mà tôi đã tạo ra. – amalloy