3 votes

Impossible d'obtenir un document aléatoire (doc) à partir d'un espace de noms

Je veux afficher une page (doc) aléatoire pour un certain espace de noms.

Le nom de fonction aléatoire que je peux obtenir :

user=> (rand-nth (keys (ns-publics 'clojure.core)))
unchecked-char

Lorsque j'essaie de passer ceci à (doc), j'obtiens ceci :

user=> (doc (rand-nth (keys (ns-publics 'clojure.core))))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)

Je suis nouveau en Clojure et je ne sais pas trop comment faire... J'ai essayé de convertir cela en regexp et d'utiliser (find-doc) mais il y a peut-être une meilleure façon de faire...

5voto

Beyamor Points 2135

Explication

Le problème est que doc est une macro, pas une fonction. Vous pouvez le vérifier à l'aide de la fonction source dans le repl.

(source doc)

; (defmacro doc
;   "Prints documentation for a var or special form given its name"
;   {:added "1.0"}
;   [name]
;   (if-let [special-name ('{& fn catch try finally try} name)]
;     (#'print-doc (#'special-doc special-name))
;     (cond
;       (special-doc-map name) `(#'print-doc (#'special-doc '~name))
;       (resolve name) `(#'print-doc (meta (var ~name)))
;       (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))

Si vous êtes nouveau dans Clojure (et dans les lisps), vous n'avez peut-être pas encore rencontré les macros. En guise d'explication rapide et dévastatrice, je dirais que là où les fonctions opèrent sur des objets de type évaluée les macros opèrent sur le code non évalué c'est-à-dire le code source lui-même.

Cela signifie que lorsque vous tapez

(doc (rand-nth (keys (ns-publics 'clojure.core))))

doc tente d'opérer sur la ligne de code réelle - (rand-nth (keys (ns-publics 'clojure.core))) - plutôt que le résultat évalué (le symbole retourné par this). Le code n'étant rien d'autre qu'une liste en Clojure, c'est pourquoi l'erreur vous dit qu'une liste ne peut pas être transformée en symbole.

Solution

Donc, ce que vous voulez vraiment faire, c'est évaluer le code, puis appeler doc sur le résultat. Nous pouvons le faire en écrivant une autre macro qui évalue d'abord le code que vous lui donnez, puis le transmet à doc .

(defmacro eval-doc
 [form]
  (let [resulting-symbol (eval form)]
   `(doc ~resulting-symbol)))

Vous pouvez passer eval-doc formes arbitraires et il les évaluera avant de les transmettre à doc . Nous sommes maintenant prêts à partir.

(eval-doc (rand-nth (keys (ns-publics 'clojure.core))))

Editer :

Bien que ce qui précède fonctionne assez bien dans la réalité, si vous utilisez une compilation anticipée, vous constaterez qu'elle produit le même résultat à chaque fois. Ceci est dû au fait que la fonction resulting-symbol en el let est produite lors de la phase de compilation. Compiler une fois à l'avance signifie que cette valeur est intégrée dans le fichier .jar. Ce que nous voulons vraiment faire, c'est pousser l'évaluation de doc au moment de l'exécution. Réécrivons donc eval-doc en tant que fonction.

(defn eval-doc
  [sym]
  (eval `(doc ~sym)))

C'est aussi simple que cela.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X