Vấn đề ở đây là tinh tế và có thể khó khăn để bẻ khóa mà không cần phải hiểu một chút về macro.
Macro thao tác cú pháp theo cùng cách mà các chức năng thao tác các giá trị. Trên thực tế, các macro chỉ là các hàm có móc làm cho chúng được đánh giá tại thời gian biên dịch. Chúng được chuyển qua dữ liệu mà bạn thấy trong mã nguồn và được đánh giá từ trên xuống. Hãy tạo hàm và macro có cùng nội dung để bạn có thể thấy sự khác biệt:
(defmacro print-args-m [& args]
(print "Your args:")
(prn args))
(defn print-args-f [& args]
(print "Your args:")
(prn args))
(print-args-m (+ 1 2) (str "hello" " sir!"))
; Your args: ((+ 1 2) (str "hello" " sir!"))
(print-args-f (+ 1 2) (str "hello" " sir!"))
; Your args: (3 "hello sir!")
Macro được thay thế bằng giá trị trả về của chúng. Bạn có thể kiểm tra quá trình này với macroexpand
(defmacro defmap [sym & args]
`(def ~sym (hash-map [email protected]))) ; I won't explain these crazy symbols here.
; There are plenty of good tutorials around
(macroexpand
'(defmap people
"Steve" {:age 53, :gender :male}
"Agnes" {:age 7, :gender :female}))
; (def people
; (clojure.core/hash-map
; "Steve" {:age 53, :gender :male}
; "Agnes" {:age 7, :gender :female}))
Tại thời điểm này, tôi có lẽ nên giải thích rằng '
gây các hình thức sau đây để được quote
d. Điều này có nghĩa là trình biên dịch sẽ đọc biểu mẫu, nhưng không thực thi nó hoặc cố gắng giải quyết các biểu tượng và vv. tức là 'conj
đánh giá thành một biểu tượng, trong khi conj
đánh giá thành một hàm. (eval 'conj)
tương đương với (eval (quote conj))
tương đương với conj
.
Với ý nghĩ đó, hãy nhớ rằng bạn không thể giải quyết một biểu tượng làm không gian tên cho đến khi nó được nhập một cách kỳ diệu vào không gian tên của bạn bằng cách nào đó. Đây là chức năng của require
. Nó lấy các biểu tượng và tìm các không gian tên mà chúng tương ứng, làm cho chúng có sẵn trong không gian tên hiện tại.
Chúng ta hãy xem những gì các ns
macro sẽ mở rộng:
(macroexpand
'(ns sample.core
(:require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
Xem cách nó được trích dẫn những biểu tượng clojure.set
và clojure.string
cho chúng ta? Tiện như thế nào! Nhưng thỏa thuận khi bạn sử dụng require
thay vì :require
là gì?
(macroexpand
'(ns sample.core
(require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
Dường như bất cứ ai đã viết ns
vĩ mô là tốt đẹp, đủ để cho chúng tôi làm điều đó cả hai phương diện, kể từ khi kết quả này là chính xác giống như trước đây. Neato!
chỉnh sửa: tvachon là đúng về chỉ sử dụng :require
vì nó là hỗ trợ chính thức hình thức chỉ
Nhưng thỏa thuận với dấu ngoặc là gì?
(macroexpand
'(ns sample.core
(:require [clojure.set]
[clojure.string])))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require '[clojure.set] '[clojure.string])))
Hóa ra chúng cũng được trích dẫn, giống như chúng tôi đã thực hiện khi chúng tôi đang thực hiện cuộc gọi độc lập tới require
.
Nó cũng chỉ ra rằng ns
không quan tâm liệu chúng tôi cung cấp cho nó danh sách (parens) hoặc vectơ (dấu ngoặc) để làm việc. Nó chỉ thấy các đối số là chuỗi các sự vật. Ví dụ, công trình này:
(ns sample.core
[:gen-class]
[:require [clojure.set]
[clojure.string]])
require
, như đã chỉ ra bởi amalloy trong các ý kiến, có ngữ nghĩa khác nhau cho vectơ và danh sách, do đó, không trộn những lên!
Cuối cùng, tại sao không có tác phẩm sau?
(ns sample.core
(:require 'clojure.string 'clojure.test))
Vâng, kể từ ns
nào chúng tôi trích dẫn cho chúng ta, các biểu tượng này được trích dẫn hai lần, đó là ngữ nghĩa khác nhau từ được trích dẫn chỉ một lần và cũng có thể là sự điên loạn tinh khiết.
conj ; => #<core$conj [email protected]>
'conj ; => conj
''conj ; => (quote conj)
'''conj ; => (quote (quote conj))
Tôi hy vọng điều này sẽ hữu ích và tôi khuyên bạn nên học cách viết macro. Họ rất vui.
bản sao có thể có của [Tại sao yêu cầu trong biểu mẫu ns hoạt động khác với hàm yêu cầu] (http://stackoverflow.com/questions/3719929/why-does-require-in-the-ns-form-behave- khác nhau-from-the-require-function) –
Hm, không thực sự hỏi về sự khác biệt của [] và sự khác biệt giữa mã repl và clj. –