什么是"要求"中的parens和括号之间的区别?

Dav*_*ams 15 clojure

我有点困惑的一件事是clojure require语句中的parens和括号之间的差异.我想知道是否有人可以向我解释这一点.例如,这些做同样的事情:

(ns sample.core
  (:gen-class)
  (:require clojure.set clojure.string))
Run Code Online (Sandbox Code Playgroud)

 (ns sample.core
  (:gen-class)
  (:require [clojure.set] 
            [clojure.string]))
Run Code Online (Sandbox Code Playgroud)

但是,这可以从repl开始

(require 'clojure.string 'clojure.test)
Run Code Online (Sandbox Code Playgroud)

但是在clj文件中失败了

(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)
    ....
Run Code Online (Sandbox Code Playgroud)

而这些apear做同样的事情:

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

(ns sample.core
  (:gen-class)
  (:require clojure.set clojure.string))
Run Code Online (Sandbox Code Playgroud)

一般来说,我不理解这一点.我理解使用,导入和要求.但是我不理解":"以及[]和'()等中的事物之间的差异.任何人都能以直观的方式阐明这个话题吗?

d.j*_*ick 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!")
Run Code Online (Sandbox Code Playgroud)

宏将由其返回值替换.您可以使用以下方法检查此过程macroexpand

(defmacro defmap [sym & args]
  `(def ~sym (hash-map ~@args))) ; 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}))
Run Code Online (Sandbox Code Playgroud)

在这一点上,我应该解释一下'导致以下形式为quoted.这意味着编译器将读取表单,但不执行它或尝试解析符号等.ie 'conj计算符号,同时conj计算函数.(eval 'conj)相当于(eval (quote conj))相当于conj.

考虑到这一点,请知道您无法将符号解析为命名空间,直到它以某种方式神奇地导入您的命名空间.这就是require函数的功能.它接受符号并找到它们对应的名称空间,使它们在当前名称空间中可用.

让我们看看ns宏扩展到了什么:

(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)))
Run Code Online (Sandbox Code Playgroud)

看看它如何引用符号clojure.setclojure.string我们?多方便啊!但是当你用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)))
Run Code Online (Sandbox Code Playgroud)

似乎编写ns宏的人都很好,让我们两个方面都这样做,因为这个结果和以前完全一样.NEATO!

编辑: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])))
Run Code Online (Sandbox Code Playgroud)

事实证明他们也被引用了,就像我们在编写独立调用时一样require.

事实证明,ns我们不关心我们是否给它列表(parens)或向量(括号).它只是把论点视为事物的序列.例如,这有效:

(ns sample.core
  [:gen-class]
  [:require [clojure.set]
            [clojure.string]])
Run Code Online (Sandbox Code Playgroud)

require正如评论中的amalloy所指出的那样,对于向量和列表有不同的语义,所以不要把它们混合起来!

最后,为什么以下不起作用?

(ns sample.core
  (:require 'clojure.string 'clojure.test))
Run Code Online (Sandbox Code Playgroud)

好吧,既然ns我们引用了这些符号,这些符号被引用两次,这在语义上不同于仅被引用一次并且也是纯粹的疯狂.

conj    ; => #<core$conj clojure.core$conj@d62a05c>
'conj   ; => conj 
''conj  ; => (quote conj)
'''conj ; => (quote (quote conj))
Run Code Online (Sandbox Code Playgroud)

我希望这会有所帮助,我绝对建议学习如何编写宏.他们非常有趣.

  • `(:require(clojure.set)(clojure.string))`根本不起作用.它是一个无操作,它恰好*看起来像它的工作,因为你选择了两个已经需要的命名空间.在一些不存在的命名空间上尝试它:它默默地成功; 在现存的名称空间中,它默默无效.这里使用parens表示前缀列表,如`(:require(clojure set string))`; 你给出的语法只适用于矢量. (2认同)