我有点困惑的一件事是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.set和clojure.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)
我希望这会有所帮助,我绝对建议学习如何编写宏.他们非常有趣.
| 归档时间: |
|
| 查看次数: |
1136 次 |
| 最近记录: |