5

我正在尝试将以下宏从 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)))

但是我一直卡在需要多一级评估的 atts 上。例如,以下需要评估 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 ">"))

(出于某种原因,我没有以与本书相同的方式执行 pair 函数,这意味着它不能正确处理 nils)

4

3 回答 3

4

您的 Clojure 定义tag引用属性映射中的所有内容,而通用 lisp 版本仅引用名称。这就是你的问题的直接来源——如果你只是把'你的矢量/地图前面的,然后摆弄map来引用第一个元素,你可能会没事的。

然而,虽然移植可能是一个很好的练习,但这段代码并不是用 Clojure 方式编写的:打印是一个令人讨厌的 ucky 副作用,使得很难使用 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,请查看Hiccupprxmldata.xml中的任何一个。

于 2011-08-06T21:53:40.107 回答
0

我可能遗漏了一些东西,但是有什么特殊原因你引用了蓝色和大而不是颜色和大小,你还在宏中引用了向量,所以如果你在向量周围加上引号,它里面的东西就不会被评估报价颜色和大你得到你想要的,


(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

只是为了记录而不是使用关键字的符号将是更惯用的clojure。

于 2011-08-06T18:49:18.820 回答
0

为了完整起见,我想要的是:

(defmacro tag [tname atts & body]
  `(do (print-tag '~tname [~@(map (fn [[h# t#]] [`'~h# t#]) (pair atts))] nil)
     ~@body
     (print-tag '~tname nil true)))
于 2011-08-06T22:20:41.240 回答