8

環境:Clojure 1.4

関数のベクトルから関数のメタデータを動的に取得しようとしています。

(defn #^{:tau-or-pi: :pi} funca "doc for func a" {:ans 42} [x] (* x x))
(defn #^{:tau-or-pi: :tau} funcb "doc for func b" {:ans 43} [x] (* x x x))

(def funcs [funca funcb])

現在、REPLでのメタデータの取得は(ある程度)簡単です。

user=>(:tau-or-pi (meta #'funca))
:pi

user=>(:ans (meta #'funca))
42

user=>(:tau-or-pi (meta #'funcb))
:tau

user=>(:ans (meta #'funcb))
43

:ansただし、メタデータから、、、:tau-or-piまたは基本を取得するためにマップを実行しようとすると:name、例外が発生します。

user=>(map #(meta #'%) funcs)
CompilerException java.lang.RuntimeException: Unable to resolve var: p1__1637# in this context, compiling:(NO_SOURCE_PATH:1) 

さらに検索を行った後、2009年の投稿(https://groups.google.com/forum/?fromgroups=#!topic/clojure/VyDM0YAzF4o)から次のアイデアを得ました。

user=>(map #(meta (resolve %)) funcs)
ClassCastException user$funca cannot be cast to clojure.lang.Symbol  clojure.core/ns-resolve (core.clj:3883)

defnマクロ(Clojure 1.4の場合)がマクロVardef一部にメタデータを配置していることを知っているdefnので、単純な(meta #'funca)ものが機能していますが、関数のメタデータを動的に取得する方法はありますか(map上記の例のように)?

構文的に何かが足りないかもしれませんが、誰かが私を正しい方向または正しいアプローチに向けることができれば、それは素晴らしいことです。

ありがとう。

4

3 に答える 3

3

式は、で生成された p1__1637# という名前のパラメーターを持つ(実際には)#(meta #'%)への呼び出しに展開されるマクロであり、その名前の var が存在しないため、このローカルパラメーターを var として使用しようとする meta on への呼び出しこのエラーが発生します。defndefgensym

関数のベクトルではなく s のベクトルから始めるvar場合は、 meta をそれらにマップするだけです。関数が呼び出されるたびに var の内容を検索するという非常にわずかな実行時コストで、関数を使用する場所ならどこでも var を (ほぼ) 使用できます。

user> (def vector-of-functions [+ - *])
#'user/vector-of-functions
user> (def vector-of-symbols [#'+ #'- #'*])
#'user/vector-of-symbols
user> (map #(% 1 2) vector-of-functions)
(3 -1 2)
user> (map #(% 1 2) vector-of-symbols)
(3 -1 2)                                                           
user> (map #(:name (meta %)) vector-of-symbols)
(+ - *)
user> 

したがって#'、元のコードにいくつかの s を追加し、余分な末尾の : を削除すると、うまくいくはずです:

user> (defn #^{:tau-or-pi :pi} funca "doc for func a" {:ans 42} [x] (* x x))
#'user/funca
user> (defn #^{:tau-or-pi :tau} funcb "doc for func b" {:ans 43} [x] (* x x x))
#'user/funcb
user> (def funcs [#'funca #'funcb])
#'user/funcs
user> (map #(meta %) funcs)
({:arglists ([x]), :ns #<Namespace user>, :name funca, :ans 42, :tau-or-pi :pi, :doc "doc for func a", :line 1, :file "NO_SOURCE_PATH"} {:arglists ([x]), :ns #<Namespace user>, :name funcb, :ans 43, :tau-or-pi :tau, :doc "doc for func b", :line 1, :file "NO_SOURCE_PATH"})
user> (map #(:tau-or-pi (meta %)) funcs)
(:pi :tau)
user> 
于 2012-12-28T19:49:00.657 に答える
2

最近、vars ではなく、関数自体にメタデータを添付する方が便利であることがわかりましたdefn

古き良きものでこれを行うことができますdef

(def funca ^{:tau-or-pi :pi} (fn [x] (* x x)))
(def funcb ^{:tau-or-pi :tau} (fn [x] (* x x x)))

ここでは、メタデータが関数に添付されており、それらのメタデータを含む関数が vars にバインドされています。

これの良いところは、メタデータを検討する際に変数について心配する必要がなくなったことです。関数には代わりにメタデータが含まれているため、関数から直接取得できます。

(def funcs [funca funcb])

(map (comp :tau-or-pi meta) funcs) ; [:pi :tau]

の構文は明らかに関数defほど洗練されていないため、使用方法によっては、関数にメタデータを添付するためdefnに再実装することに関心があるかもしれません。defn

于 2012-12-28T20:37:19.957 に答える
0

Beyamor の答えについて詳しく説明したいと思います。私が書いているいくつかのコードでは、これを使用しています:

(def ^{:doc "put the-func docstring here" :arglists '([x])}
  the-func
  ^{:some-key :some-value}
  (fn [x] (* x x)))

はい、2 つのメタデータ マップがあるのは少し扱いに​​くいです。これが私がそれをする理由です:

  1. the-func最初のメタデータがvarにアタッチされます。したがって、(doc the-func)which を使用できます:

    my-ns.core/the-func
    ([x])
      put the-func docstring here
    
  2. 2 番目のメタデータは、関数自体に添付されます。(meta the-func)これにより、以下を返すことができます。

    {:some-key :some-value}
    

要約すると、このアプローチは、REPL 内の docstring と、関数のメタデータへの動的アクセスの両方が必要な場合に便利です。

于 2013-05-22T18:29:07.933 に答える