2013-05-23 20 views
6

私はhomoiconicityの概念を誤って解釈するかもしれませんが、私はそれを 'code being data'と理解しています。Clojure関数を分解することは可能ですか?

だから、私はこのようなコードを記述することができます:heloはデータのみであるが、このようなコードとして実行することができ、この時点で

(def subject "world") 
(def helo '(str "Hello " subject)) 

を:「こんにちは、世界を返す

(eval helo) 

"

私はまた、データとしてheloの治療のために続けることができます。

(first helo) 
(count helo) 

それぞれstr3を返します。

これまでのところとても良いです。しかし、できるだけ早く私は、関数内のコードをラップするように、私は、データとしてコードを治療する能力を失っているように見える:

(defn helofn [subject] 
    (str "Hello " subject)) 

にはどうすればhelofnを分解しますか?私はそれをデータとして扱うことはできないようです。私はこれを行う場合:別の方法があるようにされているユーザの$ helofn

java.lang.UnsupportedOperationExceptionが:このタイプではサポートされていないカウント

(count helofn) 

私は例外を取得しますhelofnを分解するか、私は均質性からあまりにも多くを期待していますか?

答えて

6

defnはちょうどmacroです:

(macroexpand '(defn helofn [subject] 
    (str "Hello " subject))) 

(def helofn (clojure.core/fn ([subject] (str "Hello " subject)))) 

あなたがheloを定義しhelofn方法を定義する場合は、データとして扱うことができるようになります:

(def helofn '(fn [subject] 
    (str "Hello " subject))) 

今、あなたはevalしたり、この関数を呼び出す:

((eval helofn) "world") 

とデータとして扱うために:

(count helofn) 

をしかし、あなたがマクロdefnを使用する場合、あなたはそれのコードをコンパイルされた関数ではなくてhelofn変数を関連付けます。

これは単なる機能ではありません。あなたが次のコードでhelloを定義したとしましょう:今すぐ

(def helo (str "Hello " subject)) 

hello(str "Hello " subject)コードと文字列「Hello World」といないに関連付けられています。ですから、この文字列を組み込んだコードを取得する方法はありません。

N.B.クロージャーコードをデータとして扱う場合は、macrosを調べてください。マクロに渡されるコードはすべてデータとして扱われ、マクロによって返されるデータはコードとして扱われます。

1

基本的にあなたが.cljファイルで定義された関数からソースを取得することができます

get a clojure function's code

Can you get the "code as data" of a loaded function in Clojure?

に基づいていますが、への信頼性の高い方法はありません何のように見えません関数単独から関数を構築したデータ構造を検索します。

EDIT:また、私はあなたが同質性からあまりにも多くを期待していると思います。コード自体はデータですが、コードで生成された成果物に基づいて元のソースコードを取得できないことはかなり標準的です。私が2を持っているときと同じように、(+ 1 1)または( - 4 2)によって生成されたことを知る方法がないのは、関数が、他のいくつかのデータ構造を介してfnを呼び出して作成したデータコードとして。

9

helofn定義データですが、あなたは(あなたが明示的にheloリストを評価しただけのように)それが評価させています。あなたはheloと同じように定義を処理する場合、それはデータを残され、適用したいものは何でも変換に敏感に反応します:

(def helofndata '(defn helofn [subject] 
        (str "Hello " subject)) 

=> (second helofndata) 
helofn 
=> (eval helofndata) 
#'user/helofn 
3

Homoiconicityは非常に強力な概念であり、私はあなたが期待しているとは思いませんそれからあまりにも多く。

defnので、実際に関数を定義するdef特別なフォームを使用するマクロです:

(defn sq [x] 
    (* x x)) 

がに実際に等価です。

(def sq (fn ([x] (* x x)))) 

だからここdefnは、引数sq [x] (* x x)を受信して​​いますリスト(def sq (fn ([x] (* x x))))を作成し、それをマクロの結果として返し、評価されます。これは、リスト、マップ、ベクトル、シンボルなどを操作することによって、すべてdefnマクロによって行われます。

Clojureでは、関数を定義した元のシンボルのリストを取得できないという事実は、Clojureではすべてのコードがコンパイルされるという事実と関係しています。このため、REPLで(fn [x] 1)を評価すると#<user$eval809$fn__810 [email protected]> のようなものが返されます。しかし、previous answerに記載されているように、評価されるコードデータです。

たぶん私はこれで行き過ぎだけど、あなたが定義した各機能を、それが作成されたデータを持っているしたい場合は、独自のカスタムマクロを作成することによって、そのメタデータに追加することができます。ここで

は、このようなマクロのためのナイーブな実装です:

(defmacro defn* [x & body ] 
    (let [form `'~&form 
     x (vary-meta x assoc :form form)] 
    `(defn ~x [email protected]))) 
;=> #'user/defn* 

(defn* sq [x] 
    (* x x)) 
;=> #'user/sq 

(:form (meta #'sq)) 
;=> (defn* sq [x] (* x x)) 

&formは(& ENVと一緒に)暗黙の引数でマクロが呼び出されたときの全体(未評価の)フォームが含まれています(つまり、データをその)コンパイラによって評価されます。

は、このことができますし、それがより多くの混乱をもたらすものではありません願っています。

関連する問題