2013-05-23 13 views
6

Chociaż mogę niepoprawnie interpretować pojęcie homoiconicity, rozumiem to jako "kod będący danymi".Czy można rozłożyć funkcję Clojure?

więc mogę napisać kod jak poniżej:

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

W tym momencie helo to tylko dane, ale może być wykonany jako kod tak:

(eval helo) 

która zwraca „Hello world ".

Mogę też nadal traktować helo danych:

(first helo) 
(count helo) 

która zwraca odpowiednio str i 3.

tej pory tak dobrze. Jednak jak tylko owinąć kod w funkcji, wydaje mi się tracić zdolność do traktowania jako dane kodu:

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

Jak rozkładają helofn? Wydaje się, że nie mogę traktować tego jako danych; jeśli mogę to zrobić:

(count helofn) 

uzyskać wyjątek:

java.lang.UnsupportedOperationException: Ilość nie jest obsługiwany w tej kategorii: Użytkownik $ helofn

Czy istnieje inny sposób rozkładać helofn, czy też po prostu oczekuję zbyt wiele od homoiczności?

Odpowiedz

6

defn tylko macro:

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

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

Jeśli zdefiniujemy helofn sposób definiowania helo, będzie można ją traktować jako dane:

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

Teraz można EVAL i wywołaj tę funkcję:

((eval helofn) "world") 

i traktować je jako dane:

(count helofn) 

Ale podczas korzystania defn makro ty kojarzy helofn zmienną z funkcji skompilowany, a nie z jego kod.

To nie tylko funkcje. Powiedzmy, że określona hello z następującego kodu:

(def helo (str "Hello " subject)) 

Teraz hello jest związany z „Hello World” ciąg, a nie z kodem (str "Hello " subject). Tak więc, teraz nie ma sposobu, aby uzyskać kod, z którego ten ciąg został zbudowany.

N.B. Jeśli chcesz traktować kod clojure jako dane, powinieneś sprawdzić jego numer macros. Każdy kod przekazany do makra jest traktowany jako dane, a wszelkie dane zwracane przez makro są traktowane jako kod.

1

To wygląda nie na podstawie

get a clojure function's code

i

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

Zasadniczo można uzyskać źródło z funkcji określonej w pliku .clj ale nie ma niezawodny sposób pobrać struktury danych, które zbudowały funkcję z samej funkcji.

EDYCJA: Myślę również, że oczekujesz zbyt wiele od homoiczności. Sam kod to dane tak, ale jest dość standardowy, aby nie móc odzyskać oryginalnego kodu źródłowego w oparciu o artefakt emitowany przez ten kod. Tak jak wtedy, gdy mam 2, nie mam możliwości dowiedzenia się, że został wytworzony przez (+ 1 1) lub (- 4 2) w taki sam sposób, jak funkcja jest fragmentem danych utworzonym przez wywołanie fn nad niektórymi innymi strukturami danych, które są interpretowane jako kod.

9

helofn definicja jest danych, ale pozwalasz być oceniane (tak jak wyraźnie oceniany listę helo). Jeśli traktować definicję w taki sam sposób jak helo, to pozostanie on dane i podatne na cokolwiek transformacje chcesz zastosować:

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

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

homoikoniczność to bardzo potężne pojęcie i nie sądzę spodziewasz zbyt wiele od tego.

defn jest rzeczywiście makro, które używa def specjalny formularz, aby zdefiniować funkcję tak:

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

Czy rzeczywiście równoważne:

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

Więc defn tutaj odbiera args sq [x] (* x x), następnie buduje listę (def sq (fn ([x] (* x x)))), zwraca ją jako wynik makra, a następnie eval'ed. Wszystko to odbywa się poprzez manipulację listami, mapami, wektorami, symbolami itp. Za pomocą makra defn.

Fakt, że w Clojure nie można uzyskać oryginalnej listy symboli, z których została zdefiniowana funkcja, ma związek z faktem, że w Clojure cały kod jest kompilowany. Z tego powodu ocena (fn [x] 1) w REPL zwraca wartość podobną do #<user$eval809$fn__810 [email protected]> . Ale nadal, jak wspomniano w previous answer, kod, który jest oceniany jest danych.

Być może posunę się za daleko, ale jeśli chcesz mieć dla każdej zdefiniowanej funkcji, dane, z których zostały utworzone, możesz dodać je do swoich metadanych, tworząc własne niestandardowe makro.

Oto naiwna implementacja dla takiego makra:

(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 jest niejawny argument (wraz z & ENV), który zawiera całość (unevaluated) postać, z którą makro nazwano (czyli dane, które jest oceniane przez kompilator).

Mam nadzieję, że to pomaga i nie powoduje więcej zamieszania.

Powiązane problemy