2013-04-09 6 views
15

내가 혼란스러워했던 한 가지는 clojure require 문에서 괄호와 괄호 사이의 차이입니다. 나는 누군가가 나에게 이것을 설명 할 수 있는지 궁금해했다. 예를 들어,이 같은 일을 수행"요구 사항"에서 괄호와 괄호의 차이점은 무엇입니까?

(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

및 그러나

(ns sample.core 
    (:gen-class) 
    (:require [clojure.set] 
      [clojure.string])) 

는, 이것이 CLJ 파일에 REPL

(require 'clojure.string 'clojure.test) 

에서 작동하지만 실패를

(ns sample.core 
    (:gen-class) 
    (:require 'clojure.string 'clojure.test)) 
... 
Exception in thread "main" java.lang.Exception: lib names inside prefix lists must not contain periods 
    at clojure.core$load_lib.doInvoke(core.clj:5359) 
    at clojure.lang.RestFn.applyTo(RestFn.java:142) 
    .... 

반면에 이러한 apear 같은 얇은 할 g :

(ns sample.core 
    (:gen-class) 
    (require clojure.set clojure.string)) 

(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

일반적으로 나는 이것을 이해하지 못합니다. 사용, 수입 및 요구 사항을 이해합니다. 하지만 나는 ":"과 []와 [() 등의 사물의 차이점을 이해하지 못합니다. 누구든지이 주제를 직관적 인 방식으로 밝힐 수 있습니까?

+0

[ns 폼에서 require가 require 함수와 다른 동작을하는 이유는 무엇입니까?] (http://stackoverflow.com/questions/3719929/why-does-require-in-the-ns-form-behave- different-from-the-require-function) –

+1

음, []와 repl과 clj 코드의 차이에 대해서는 정말로 묻지 않습니다. –

답변

12

여기에있는 문제는 매크로에 대해 약간 이해하지 않고서는 미묘하고 어려울 수도 있습니다.

매크로는 함수가 값을 조작하는 것과 같은 방법으로 구문을 조작합니다. 실제로 매크로는 컴파일 타임에 평가되는 후크가있는 함수 일뿐입니다. 소스 코드에서 볼 수 있고 하향식으로 평가되는 데이터 리터럴이 전달됩니다. 차이점을 볼 수 있도록 같은 본문을 가진 함수와 매크로를 만들어 봅시다 :

(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)) 

난이 도움이 되었으면 좋겠, 나는 확실히 매크로를 작성하는 방법을 학습하는 것이 좋습니다. 그들은 슈퍼 재미입니다.

+2

'(: require (clojure.set) (clojure.string))'이 전혀 작동하지 않습니다. 그것은 이미 요구되는 두 개의 네임 스페이스를 선택했기 때문에 작동하는 것처럼 보이지 않는 일입니다. 존재하지 않는 네임 스페이스에서 시도해보십시오. 자동으로 성공합니다. 현존하는 네임 스페이스에서는 아무것도하지 않습니다. 여기에 괄호를 사용하면'(: require (clojure set string))'과 같이 접두사 목록을 나타냅니다. 구문은 벡터로만 작동합니다. – amalloy

+0

잘 찾아 냈습니다. 내가 반영 할 소식을 수정하겠습니다. –

+0

위대한 답변, +1이 TDT 참조 인 경우 해당 내용이 – Hendekagon

4

TL; DR :

 
(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

 
(ns sample.core 
    (:gen-class) 
    (:require [clojure.set] 
      [clojure.string])) 

모두 괜찮다 - 두 번째 버전은 가장 유연한 문법 require 지지부의 특별한 경우이다. 또한 다음과 같이 쓸 수 있습니다.

 
(ns sample.core 
    (:gen-class) 
    (:require [clojure set string])) 

일반적으로이 마지막 양식은이 특정 요구 사항에 대한 모범 사례입니다.


(require 'clojure.string 'clojure.test)

는 또한 CLJ 파일에서 작동 -이 시도 :

 
(ns sample.core 
    (:gen-class)) 
(require 'clojure.string 'clojure.test) 

여기에 혼란이 깨진 예를 들어 당신이에서 "인용 기호를"사용하려는 것을 ns 매크로의 :require 절. 그것은 아마도 가장 직관적 인 설명은 아니지만 다음은 어떻게 분해되는지입니다 :

다른 모듈을 요구하는 데는 두 가지 방법이 있습니다. requirens.

require은 따옴표로 묶은 양식 목록을 취하는 기능입니다.이 기호는 클로저가 require에 전달하는 기호를 다른 모든 기호를 찾는 방식으로 검색하지 않도록하기 위해 필요합니다.

ns:require 옵션을 지원하는 매크로입니다. 이 옵션의 가치를 취하고이를 커버하여 require 함수 호출로 변환합니다. ns은 매크로이므로 기호 자체를 인용 할 수 있기 때문에 :require 옵션의 값을 인용 할 필요가 없습니다.

아직 명확하지 않을 수도 있지만 명확히하기 위해 Clojure 문서로 돌아가는 것이 좋습니다. 일단 Clojure에 대해 전반적으로 더 잘 이해하게되면 완전히 이해할 수 있습니다.

Clojure 소스 파일에서 라이브러리를 요구하려면 항상 ns 절을 사용해야합니다. require은 REPL에서만 사용해야합니다. 마지막 두 예에서


당신은 올바른 그

 
(ns sample.core 
    (:gen-class) 
    (require clojure.set clojure.string)) 

작품,하지만이 사고입니다 -

 
(name :require) 
=> "require" 

(name 'require) 
=> "require" 

문서화 된 구문은 사실 아마 결과

 
(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

이며 앞으로도 깨지 않을 수있는 유일한 제품입니다.

+0

"Clojure 소스 파일에서 라이브러리를 요구하려면 항상 ns 절을 사용해야하며 REPL에서만 require를 사용해야합니다." 왜? –

+2

기술적 인 이유가 없습니다. 스타일과 가독성 중 하나입니다. 'ns'를 일관성있게 사용하면 다른 프로그래머가 파일 전체에서 파기하지 않고 파일에 필요한 네임 스페이스를 쉽게 볼 수 있습니다. 수동으로 양식을 벗어날 필요가 없기 때문에 조금 더 깨끗합니다. – tvachon

관련 문제