我正在尝试将以下宏从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)))
但我一直陷入需要更多评估等级的赌注。例如。以下需要评估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 ">"))
(出于某种原因,我没有像书中那样执行配对功能,这意味着它没有正确处理nils)
答案 0 :(得分:4)
您对Clojure tag
的定义引用了属性映射中的所有内容,而常见的lisp版本仅引用了名称。这是你问题的直接来源 - 如果你只是将'
放在你的矢量/地图前面,然后摆弄map
以引用第一个元素,你可能会没事。< / p>
然而,虽然移植可能是一个很好的练习,但这段代码不是用Clojure方式编写的:打印是一种令人讨厌的副作用,这使得难以使用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,请查看Hiccup,prxml或data.xml中的任何内容。
答案 1 :(得分: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
仅仅为了记录而不是使用关键字的符号,这将是更为惯用的方法。
答案 2 :(得分: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)))