Оценка макро аргументов в clojure - PullRequest
5 голосов
/ 06 августа 2011

Я пытаюсь перевести следующий макрос из lisp в 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)))

Но я продолжаю зацикливаться на умениях, требующих еще 1 уровня оценки. Например. для оценки t # необходимо следующее:

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

Как он производит такие вещи, как:

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

Где я хочу, чтобы атрибут оценивался. Если я использую "(eval t #)" в вышеупомянутом, я падаю на проблемы, подобные этой:

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

Есть предложения?

Почему кажется, что в Clojure происходит еще один уровень оценки?

Определения вспомогательных функций:

;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 ">"))

(По какой-то причине я не выполнял парную функцию так же, как в книге, что означает, что она неправильно обрабатывает ниль)

Ответы [ 3 ]

4 голосов
/ 07 августа 2011

Ваше определение Clojure tag цитирует все на карте атрибутов, в то время как общая версия lisp цитирует только имена.Это непосредственный источник ваших проблем - если вы просто уроните ' перед вектором / картой, а затем возьмете map, чтобы процитировать первый элемент, вы, вероятно, будете в порядке.

Однако, хотя портирование может быть хорошим упражнением, этот код не написан в Clojure Way: печать - неприятный неприятный побочный эффект, который затрудняет использование print-tag для выполнения чего-либо значимого;вместо этого возвращение строки было бы намного приятнее.

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

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

Конечно, поскольку порядок не имеет значения, использование карты вместо вектора лучше для атрибутов.Это также означало бы, что вы можете отбросить (partition 2...), поскольку последовательное представление карты уже выглядит как пары.

И как только мы дошли до этого, оказывается, что уже есть много способовпредставлять XML как структуры данных Clojure, поэтому я никогда не использовал бы свой код выше в реальном приложении.Если вы хотите сделать XML по-настоящему, проверьте любой из Икота , prxml или data.xml .

0 голосов
/ 07 августа 2011

Ради полноты, то, что я хотел, оказалось:

(defmacro tag [tname atts & body]
  `(do (print-tag '~tname [~@(map (fn [[h# t#]] [`'~h# t#]) (pair atts))] nil)
     ~@body
     (print-tag '~tname nil true)))
0 голосов
/ 06 августа 2011

Возможно, я что-то упустил, но есть ли какая-то конкретная причина, по которой вы указали синий и большой, но не цвет и размер, вы также указали в макросе вектор, чтобы содержимое внутри него не оценивалось, если вы отбросите кавычку вокруг вектор, а также цитата цвета и большой вы получаете то, что вы хотите,


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

(tag mytag ['color 'blue 'size 'big])

<mytag color="blue" size="big"><\mytag>nil

Просто для записи вместо символов использование ключевых слов было бы более идиоматическим замыканием для этого.

...