여기에있는 문제는 매크로에 대해 약간 이해하지 않고서는 미묘하고 어려울 수도 있습니다.
매크로는 함수가 값을 조작하는 것과 같은 방법으로 구문을 조작합니다. 실제로 매크로는 컴파일 타임에 평가되는 후크가있는 함수 일뿐입니다. 소스 코드에서 볼 수 있고 하향식으로 평가되는 데이터 리터럴이 전달됩니다. 차이점을 볼 수 있도록 같은 본문을 가진 함수와 매크로를 만들어 봅시다 :
(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!")
매크로는 반환 값으로 대체됩니다. 당신은이 시점에서 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}))
으로이 과정을 검사 할 수 있습니다, 난 아마 '
가 quote
D로 다음 양식을 유발한다는 설명해야한다. 즉, 컴파일러는 양식을 읽지 만 실행하지 않거나 기호 등을 해결하려고하지는 않습니다. 즉 'conj
은 기호로 평가되는 반면 conj
은 함수로 평가됩니다. (eval 'conj)
은 (eval (quote conj))
에 해당하며 conj
에 해당합니다.
마음에두고, 당신의 네임 스페이스에 마술처럼 여겨 질 때까지 네임 스페이스로서 심볼을 해석 할 수 없다는 것을 알아라. 이것이 require
함수의 기능입니다. 심볼을 취하여 해당 네임 스페이스를 찾아 현재 네임 스페이스에서 사용할 수 있도록합니다. 이 기호 clojure.set
및 우리 clojure.string
을 인용하는 방법을 참조하십시오
(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)))
:
는의가
ns
매크로로 확장 무엇을 보자? 얼마나 편리한 지! 그러나
:require
대신
require
을 사용할 때 어떤 문제가 발생합니까?
(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)))
이 결과는 정확하게 이전과 동일하기 때문에 누구든지 ns
매크로를 작성하는 것은, 우리가 그것을 두 가지를 할 수 있도록 충분히 좋은 것을 보인다. 네타!
편집 : tvachon는 유일하게 공식적으로 지원 형태
입니다 그러나 브래킷 거래 무엇 이후에만 :require
사용에 대한 권리?
(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])))
우리가 require
에 독립형 호출을 쓰고 있다면 우리가 할 것처럼, 그들은 너무 인용 얻을 밝혀졌습니다.
또한 ns
은 목록 (괄호) 또는 벡터 (대괄호)와 함께 작업할지 여부는 신경 쓰지 않습니다. 그것은 단지 사물을 일련의 것으로 간주합니다. 예를 들어,이 작품 : 코멘트에 amalloy에 의해 지적
require
(ns sample.core
[:gen-class]
[:require [clojure.set]
[clojure.string]])
, 벡터 및 목록에 대해 서로 다른 의미를 가지고, 그래서 사람들을 함께 사용하지 마십시오!
마지막으로 다음 작업이 왜 효과가 있습니까? ns
이후
(ns sample.core
(:require 'clojure.string 'clojure.test))
글쎄, 우리 우리를 인용,이 기호는 한 번만 인용되는 것을 의미 상 차이가 있고 순수한 막무가내 인, 두 번 인용 얻을 않습니다.
conj ; => #<core$conj [email protected]>
'conj ; => conj
''conj ; => (quote conj)
'''conj ; => (quote (quote conj))
난이 도움이 되었으면 좋겠, 나는 확실히 매크로를 작성하는 방법을 학습하는 것이 좋습니다. 그들은 슈퍼 재미입니다.
[ns 폼에서 require가 require 함수와 다른 동작을하는 이유는 무엇입니까?] (http://stackoverflow.com/questions/3719929/why-does-require-in-the-ns-form-behave- different-from-the-require-function) –
음, []와 repl과 clj 코드의 차이에 대해서는 정말로 묻지 않습니다. –