gpt4 book ai didi

clojure - "require"中的括号和括号有什么区别?

转载 作者:行者123 更新时间:2023-12-03 20:39:22 26 4
gpt4 key购买 nike

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

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


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

但是,这适用于 repl
(require 'clojure.string 'clojure.test)

但在 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)
....

而这些似乎做同样的事情:
(ns sample.core
(:gen-class)
(require clojure.set clojure.string))

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

一般来说,我不明白这一点。我了解使用、导入和要求。但我不明白“:”以及 [] 和 '() 等内容之间的区别。谁能以直观的方式阐明这个主题?

最佳答案

这里的问题很微妙,如果不先了解一下宏,可能很难理解。

宏操作语法的方式与函数操作值的方式相同。事实上,宏只是带有一个钩子(Hook)的函数,它可以在编译时对它们进行评估。它们传递您在源代码中看到的数据文字,并自上而下进行评估。让我们创建一个具有相同主体的函数和一个宏,这样您就可以看到区别:

(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 ~@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}))

在这一点上,我大概应该解释一下 '导致以下形式为 quote d。这意味着编译器将读取表单,但不会执行它或尝试解析符号等。即 '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)))

看看它是如何引用符号的 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)))

似乎是谁写了 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不在乎我们是否给它列表(括号)或向量(括号)来使用。它只是将参数视为事物的序列。例如,这有效:
(ns sample.core
[:gen-class]
[:require [clojure.set]
[clojure.string]])
require ,正如 amalloy 在评论中指出的那样,向量和列表具有不同的语义,所以不要混淆它们!

最后,为什么以下工作不起作用?
(ns sample.core
(:require 'clojure.string 'clojure.test))

好吧,因为 ns我们为我们引用,这些符号被引用两次,这在语义上与只引用一次不同,也是纯粹的疯狂。
conj    ; => #<core$conj clojure.core$conj@d62a05c>
'conj ; => conj
''conj ; => (quote conj)
'''conj ; => (quote (quote conj))

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

关于clojure - "require"中的括号和括号有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15905148/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com