2011-08-06 18 views
5

Próbuję tłumaczyć następujące makro z ziemi Lisp w Clojure:Analizowanie makro argumenty Clojure

(defmacro tag (name atts &body body) 
    `(progn (print-tag ',name 
        (list ,@(mapcar (lambda (x) 
             `(cons ',(car x) ,(cdr x))) 
            (pairs atts))) 
        nil) 
      ,@body 
      (print-tag ',name nil t))) 

ale wciąż utknięcie z atts wymagając 1 poziom oceny. Na przykład. następujące potrzeby oceny T #:

(defmacro tag [tname atts & body] 
    `(do (print-tag '~tname '[[email protected](map (fn [[h# t#]] [h# t#]) (pair atts))] nil) 
    [email protected] 
    (print-tag '~tname nil true))) 

ponieważ wytwarza rzeczy jak:

(tag mytag [color 'blue size 'big]) 
<mytag color="(quote blue)" size="(quote big)"><\mytag> 

gdzie chcę atrybut do oceny. Jeśli użyję "(eval t #)" w powyższym, będę miał problemy takie jak to:

(defn mytag [col] (tag mytag [colour col])) 
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:1) 

Jakieś sugestie?

Dlaczego w Clojure zdarza się mniejszy poziom oceny?

Definicje funkcji wspomagających:

;note doesn't handle nils because I'm dumb 
(defn pair [init-lst] 
     (loop [lst init-lst item nil pairs []] 
    (if (empty? lst) 
     pairs 
     (if item 
     (recur (rest lst) nil (conj pairs [item (first lst)])) 
     (recur (rest lst) (first lst) pairs))))) 

(defn print-tag [name alst closing] 
     (print "<") 
     (when closing 
    (print "\\")) 
     (print name) 
     (doall 
     (map (fn [[h t]] 
      (printf " %s=\"%s\"" h t)) 
     alst)) 
     (print ">")) 

(Z jakiegoś powodu nie zrobił funkcję pary w taki sam sposób, jak w książce, co oznacza, że ​​nie obsługuje Nils poprawnie)

+0

Jak '' pair' i druku tag' zdefiniowane? –

+0

@ Brian Dodałem te definicje do posta –

+0

wydaje mi się, że 'pair' jest to samo' '(partycja 2 seq)', prawda? – skuro

Odpowiedz

4

Twoja definicja Clojure z tag cytuje wszystko w mapie atrybutów, podczas gdy standardowa wersja lisp podaje tylko nazwy. To jest bezpośrednie źródło twoich problemów - jeśli po prostu upuściłeś ' przed swoim wektorem/mapą, a potem zacząłeś manipulować przy map, aby zacytować pierwszy element, prawdopodobnie byłbyś w porządku.

Jednakże, chociaż portowanie może być dobrym ćwiczeniem, ten kod nie jest napisany w The Clojure Way: drukowanie jest nieprzyjemnym efektem ubocznym, który sprawia, że ​​trudno jest użyć znacznika drukowania, aby zrobić cokolwiek znaczącego; zwrócenie napisu zamiast tego byłoby znacznie ładniejsze.

(defmacro tag [name attrs & body] 
    `(str "<" 
     (clojure.string/join " " 
          ['~name 
           [email protected](for [[name val] (partition 2 attrs)] 
            `(str '~name "=\"" ~val "\""))]) 
     ">" 
     [email protected] 
     "</" '~name ">")) 

user> (tag head [foo (+ 1 2)] "TEST" (tag sample [])) 
"<head foo=\"3\">TEST<sample></sample></head>" 

Oczywiście, ponieważ kolejność nie ma znaczenia, użycie mapy zamiast wektora jest przyjemniejsze dla atrybutów. Oznaczałoby to również, że można upuścić model (partition 2...), ponieważ widok sekwencyjny mapy jest już wyświetlany jako para.

A kiedy dotarliśmy tak daleko, okazuje się, że istnieje już wiele sposobów na reprezentowanie XML jako struktur danych Clojure, więc nigdy nie użyłbym powyższego kodu w prawdziwej aplikacji. Jeśli chcesz, aby XML działał naprawdę, sprawdź wszystkie z Hiccup, prxml lub data.xml.

+0

Tak, zgadzam się, że byłoby znacznie lepiej użyć mapy. Również użycie słów kluczowych sugerowanych przez Hamza Yerlikaya byłoby usprawnieniem. Nie jestem przekonany o zwrocie struny - wolałbym raczej wydrukować ją bezpośrednio do strumienia (chociaż używanie druku również nie jest właściwe). W końcu jednak próbowałem zrozumieć makra clojure i porównać je do seplenienia. –

+0

Dzięki za te linki btw. Wydaje się, że prxml to podejście wykonane właściwie. Jedną z tych fajnych rzeczy na temat clojure jest to, że łatwo jest je czytać i uczyć się z kodu innych ludzi. –

+0

Nie jestem pewien co do "właściwego" - prxml jest najbardziej efektownym z moich sugestii, a jako część starego contrib jest przestarzałe. data.xml jest przyszłością i powinien zawierać wszystkie funkcje prxml w bardziej elastycznym pakiecie. – amalloy

0

I może czegoś brakuje, ale czy jest jakiś szczególny powód, dla którego cytowałeś kolor niebieski i duży, ale nie kolor i rozmiar, cytowałeś również w makrze wektor, więc rzeczy w nim nie zostaną ocenione, jeśli zrzucisz cytat z wektora, a także zacytujesz kolor i duży masz to, czego chcesz,

 

(defmacro tag [tname atts & body] 
    `(do (print-tag '~tname [[email protected](map (fn [[h# t#]] [h# t#]) (pair atts))] nil) 
     [email protected] 
     (print-tag '~tname nil true))) 

(tag mytag ['color 'blue 'size 'big]) 
 
<mytag color="blue" size="big"><\mytag>nil 

Tylko dla zapisu zamiast symboli za pomocą słów kluczowych byłoby bardziej idiomatyczne clojure dla tego.

+0

Chodzi o to, że nazwy atrybutów są rzadko obliczane, ale ich wartości to np. (prostokąt znacznika [wysokość (+ 3 4)]). Nie wymaganie cytatu przed nazwą atrybutu jest tylko trochę bardziej zwięzłe. –

0

Dla kompletności, co chciałem okazały się:

(defmacro tag [tname atts & body] 
    `(do (print-tag '~tname [[email protected](map (fn [[h# t#]] [`'~h# t#]) (pair atts))] nil) 
    [email protected] 
    (print-tag '~tname nil true))) 
+0

To się nie skomplikuje, więc wątpię w to. Twoja sztuczka '' '~' należy do * drugiej * instancji 'h # ', a nie do pierwszej. – amalloy

+0

Prawda. To dziwne, myślałem, że je wycinam i wklejam ... Naprawiono. –

Powiązane problemy