我正在尝试从Hy生成一些python代码,如何做得更好?在
我试过几种方法。一种是使用宏:
(defmacro make-vars [data]
(setv res '())
(for [element data]
(setv varname (HySymbol (+ "var" (str element))))
(setv res (cons `(setv ~varname 0) res)))
`(do ~@res))
然后在捕获宏扩展之后,打印代码的python反汇编。在
但是,对于宏,我似乎无法传递变量,因此:
^{pr2}$定义varv
、varn
、vara
等,代替var1
、var2
、var3
。似乎可以通过以下方式进行正确的调用:
(macroexpand `(make-vars ~vnames))
但这似乎过于复杂。在
我遇到的另一个问题是HySymbol
的必要性,这让我大吃一惊。但当我尝试第二种方法时,我真的受到了伤害,在第二种方法中,我创建了一个返回引用形式的函数:
(defn make-faction-detaches [faction metadata unit-types]
(let [meta-base (get metadata "Base")
meta-pattern (get metadata "Sections")
class-cand []
class-def '()
class-grouping (dict)]
(for [(, sec-name sec-flag) (.iteritems meta-pattern)]
;; if section flag is set but no unit types with the section are found, break and return nothing
(print "checking" sec-name)
(if-not (or (not sec-flag) (any (genexpr (in sec-name (. ut roles)) [ut unit-types])))
(break)
;; save unit types for section
(do
(print "match for section" sec-name)
(setv sec-grouping (list-comp ut [ut unit-types]
(in sec-name (. ut roles))))
(print (len sec-grouping) "types found for section" sec-name)
(when sec-grouping
(assoc class-grouping sec-name sec-grouping))))
;; in case we finished the cycle
(else
(do
(def
class-name (.format "{}_{}" (. meta-base __name__) (fix-faction-string faction))
army-id (.format "{}_{}" (. meta-base army_id) (fix-faction-string faction))
army-name (.format "{} ({})" (fix-faction-name faction) (. meta-base army_name)))
(print "Class name is" class-name)
(print "Army id is" army-id)
(print "Army name is" army-name)
(setv class-cand [(HySymbol class-name)])
(setv class-def [`(defclass ~(HySymbol class-name) [~(HySymbol (. meta-base __name__))]
[army_name ~(HyString army-name)
faction ~(HyString faction)
army_id ~(HyString army-id)]
(defn --init-- [self]
(.--init-- (super) ~(HyDict (interleave (genexpr (HyString k) [k class-grouping])
(cycle [(HyInteger 1)]))))
~@(map (fn [key]
`(.add-classes (. self ~(HySymbol key))
~(HyList (genexpr (HySymbol (. ut __name__))
[ut (get class-grouping key)]))))
class-grouping)))]))))
(, class-def class-cand)))
该函数获取的元数据在python中如下所示:
metadata = [
{'Base': DetachPatrol,
'Sections': {'hq': True, 'elite': False,
'troops': True, 'fast': False,
'heavy': False, 'fliers': False,
'transports': False}}]
并获取具有以下形式的类的列表:
class SomeSection(object):
roles = ['hq']
它需要大量使用hy的内部类,我无法正确地表示真与假,而是求助于HyInteger(1)
和{
为了从这个函数得到python代码,我通过disassemble
运行它的结果。在
总结如下:
在Hy中,通常不需要生成Python代码,因为Hy在生成Hy代码方面更好,而且它也是可执行的。这在Hy宏中一直都是这样。在
在不寻常的情况下,您需要生成真正的Python,而不仅仅是Hy,最好的方法是使用字符串,这与在Python中进行生成的方法相同。Hy编译为Python的AST,而不是Python本身。反汇编程序实际上只是为了调试目的。它并不总是生成有效的Python:
变量名
+!@$
与AST中的spam
一样合法,但是Python的exec
阻塞了它,因为它不是一个有效的Python标识符。在如果您理解这个限制,并且可以使用
disassemble
,但不使用宏。允许普通运行时函数获取和生成(正如您演示的那样)Hy表达式。宏实际上只是这样的函数,而不是在编译时运行。在Hy中,宏将其部分工作委托给一个普通函数,该函数将Hy表达式作为其参数之一并返回Hy表达式。在创建Hy表达式作为数据最简单的方法是用
^{pr2}$'
引用它。用于插值值的反勾语法即使在宏体之外也有效。您也可以在正常的运行时函数中使用它。但是要明白,如果要反汇编插值,必须在插值中插入引用的形式,因为这是宏接收的参数,即代码本身,而不是其计算值。这就是为什么你使用HySymbol
和朋友。在您可以询问REPL它使用什么类型的引用表单。在
如您所见,
True
在内部只是一个符号。请注意,我可以只使用'
生成一个HySymbol
,而不使用HySymbol
调用。如果您的元数据文件是用Hy编写的,并且最初是用带引号的Hy格式生成的,那么您就不必转换它们了。但没有理由必须在最后一刻在反勾形式内完成。如果您愿意的话,可以通过helper函数提前完成。在随访
我的原意是宏对于你所要做的是错误的工具。但是为了澄清这一点,您可以在运行时通过使用
macroexpand
调用宏,正如您已经演示的那样。当然,您可以将macroexpand
调用放在另一个函数中,但是macroexpand
必须有一个带引号的形式作为它的参数。在字典的部分可以简化为
Python的
dict
是由哈希表支持的,而Hy的HyDict
模型实际上只是一个列表。这是因为它不代表哈希表本身,而是生成dict的代码,这就是为什么可以像列表一样拼接到它。在Hy's models被认为是公共API的一部分,它们只是在宏之外很少使用。当你需要的时候使用它们是很好的。其他lisp在代码模型对象和它们生成的数据之间没有同样的区别。Hy这样做是为了更好地实现Python互操作。有人可能会争辩说,
~
语法应该为某些数据类型自动进行这种转换,但目前,它没有。 [更新:在当前的主分支上,Hy的编译器将在Hy模型中自动包装兼容的值,因此您通常不必再亲自这样做了。]HySymbol
适合于像您尝试的那样从字符串动态生成符号。这不是唯一的办法,但这是你想要的。另一种方法,gensym
,在宏中使用得更为频繁,但是它们不能那么漂亮。您可以使用字符串调用gensym
,为调试目的给它一个更有意义的名称,但它仍然有一个数字后缀使其唯一。当然,您可以为HySymbol
分配一个较短的别名,或者将该部分委托给一个helper函数。在你也可以把它转换成前进,例如,碎片
可能是
那你就不用再做两次了。在
这可能会使模板更易于阅读。在
更新
Hy master现在在编译时将符号转换为有效的Python标识符,因此
hy2py
工具和astor反汇编应该更可靠地生成有效的Python代码,即使符号中有特殊字符。在相关问题 更多 >
编程相关推荐