How can I apply clojure's doc function to a se

2019-07-23 03:26发布

问题:

I was trying to do something I thought would be simple but has turned out to be not so and I think the solution might give me a deeper understanding of clojure so I figured I'd ask here.

I want to be able to run the following from a clojure REPL:

(map #(doc %) v)

where v is a vector of...something, I'm not sure what. The goal was to print out the doc strings for some sequence of functions, but I'm not sure how to express that vector. I've tried a couple of things: (I'll pick a few functions out at random)

[+ - first set]
['+ '- 'first 'set]
[`+ `- `first `set]
[(var +) (var -) (var first) (var set)]
[`~+ `~- `~first `~set]

None work. I also tried to apply the doc function but that doesn't work since doc is a macro and as such cannot be an argument to apply. What am I missing?

回答1:

(doseq [f ['+ '- 'first 'set]] (eval (list 'doc f)))


回答2:

In Clojure 1.2 you can use the function print-doc:

(print-doc #'fisrt)

like this

(map print-doc [#'fisrt #'+])

#'fisrt is the same as (var first).

Note that this will not work for special forms: (doc def) works, but (print-doc #def)` gives:

java.lang.Exception: Unable to resolve var: def in this context (NO_SOURCE_FILE:7)

This is because special forms aren't defined in vars. You can see how doc works for them by using macroexpand-1:

user=> (macroexpand-1 '(doc def))
(clojure.core/print-special-doc (quote def) "Special Form" (clojure.core/special-form-anchor (quote def)))

In Clojure 1.3 print-doc is moved in the clojure.repl namespace and is private, so you cannot execute it directly. You can enter the ns:

user=> (in-ns 'clojure.repl)
clojure.repl=> (print-doc (meta #'first))
clojure.repl=> (map #(print-doc (meta %)) [#'first #'+])

As you can see, Clojure 1.3 requires getting the metadata explicitly.

If you need to execute this from a different namespace you'll have to export the function. One way to do so is to define a new function:

clojure.repl=> (defn my-print-doc [m] (print-doc m))
user=> (clojure.repl/my-print-doc (meta #'first))


回答3:

(defn my-doc [v] (-> v meta :doc))

This function takes the meta data of a var and then gets the doc string out of the meta data. You can map it over a sequence of vars:

(map my-doc [(var +) (var -) (var first) (var set)]) ;=> LazySeq of doc-strings

or shorter:

(map my-doc [#'+ #'- #'first #'set]) ;=> LazySeq of doc-strings

This doesn't work for special forms, since they are not referenced by vars.