我试图通过在REPL中调用以下表达式来打印给定命名空间中所有函数的文档:
(doseq
[f (dir-fn 'clojure.repl)]
(doc f))
但是,调用此表达式会返回nil
,而不会将文档打印到REPL 。我知道这可能与doc
是一个宏有关,但我是一个Clojure新手,并不完全确定如何理解这个问题。
nil
?谢谢!
更新:结合两个提供的答案:
(defn ns-docs [ns']
(doseq [[symbol var] (ns-interns ns')]
(newline)
(println symbol)
(print " ")
(println (:doc (meta var)))))
(ns-docs 'clojure.repl)
答案 0 :(得分:2)
相反,我会从这里开始:
请注意doc
位于命名空间clojure.repl
中,它反映了其预期用途(由repl中的人员执行)。这里有一些代码也将在命名空间上进行迭代。打印doc字符串(使用不同的技术):
(doseq [[fn-symbol fn-var] (ns-interns 'demo.core)]
(newline)
(println fn-symbol)
(println (:doc (meta fn-var))))
其中demo.core
是感兴趣的名称空间。
请注意ns-interns
同时为您提供符号和var:
fn-symbol => <#clojure.lang.Symbol -main>
fn-var => <#clojure.lang.Var #'demo.core/-main>
meta
函数有很多其他信息,您可能希望有一天使用它们:
(meta fn-var) =>
<#clojure.lang.PersistentArrayMap
{ :arglists ([& args]),
:doc "The Main Man!",
:line 9, :column 1,
:file "demo/core.clj",
:name -main,
:ns #object[clojure.lang.Namespace 0x14c35a06 "demo.core"]}>
答案 1 :(得分:2)
虽然这可能无法帮助您回答问题,但在您学习Clojure时,评估宏的问题会出现很多。
宏负责评估他们的论点。在这种情况下,clojure.repl/doc
将忽略当前的词汇上下文,并假设您提供的符号f
是您要查看其文档的函数的名称。这样做是因为它打算在REPL中使用,并且假设你不想一直输入引号。
由于f
不存在,因此不会输出任何内容。然后doseq
会返回nil
,因为它只存在于副作用中 - 因此从do
开始。为了将参数传递给拒绝遵守这样的词汇上下文的宏,您需要为列表中的每个元素编写代码。
您可以手动执行此操作,也可以将代码构建为数据,并将其传递给eval
以执行。您可以使用doseq
:
(doseq [f (ns-interns 'clojure.repl)]
(eval `(doc ~(symbol "clojure.repl" (str (first f))))))
或稍微更多的Clojurey方式(这将允许您通过从末尾删除eval
并在REPL处运行它来查看它将执行的代码):
(->> (ns-interns 'clojure.repl)
(map #(list 'clojure.repl/doc (symbol "clojure.repl" (str (first %)))))
(cons `do)
eval)
在这两个中,我们使用quote和syntax-quote从命名空间反映的符号列表中构造一些代码,并将其传递给eval
以实际执行它。 This page on Clojure's weird characters应该指出你正确的方向来理解这里发生了什么。
这是一个为什么你不应该写宏的例子,除非你没有其他选择。 Macro不会编写,而且通常很难处理。要进行更深入的讨论,Fogus's talk和Christophe Grand's talk都是很好的对话。
答案 2 :(得分:1)
为什么这个表达式在没有打印文档的情况下返回nil?
因为doc
宏从循环中接收符号f
,而不是直接接收函数符号。
如何修改此表达式,以便打印给定命名空间中每个函数的文档?
(defn ns-docs [ns']
(let [metas (->> (ns-interns ns') (vals) (map meta) (sort-by :name))]
(for [m metas :when (:doc m)] ;; you could filter here if you want fns only
(select-keys m [:name :doc]))))
(ns-docs 'clojure.repl)
=>
({:name apropos,
:doc "Given a regular expression or stringable thing, return a seq of all
public definitions in all currently-loaded namespaces that match the
str-or-pattern."}
...
)
然后,您可以根据需要打印这些地图/字符串。