我正在编写宏以简化使用matplotlib进行绘图的过程。我的第一次尝试如下,可以正常工作:
(defmacro insert-ax [body] `((getattr g!ax (str '~(first body))) ~@(rest body)))
(defmacro/g! plot [main &optional title [fig-kwargs {}]]
`(do
(import [matplotlib.pyplot :as plt] [time [ctime]])
(setv [g!fig g!ax] (plt.subplots #**~fig-kwargs))
(insert-ax ~main)
(when ~title (.set-title g!ax ~title))
(.savefig g!fig (if ~title ~title (ctime)))))
然后下面的代码按预期工作:
(plot (scatter xs ys) "Data"))
(在惯用的Python中)等效于
fig, ax = plt.subplots()
ax.scatter(xs,ys)
ax.set_title("Data")
fig.savefig("Data")
这很好,但是我希望能够传递多种形式,每种形式都可以用insert-ax
进行变换,因此我可以向ax
添加多个图,传递其他选项,等等。具体来说就是do-plot
这样
(do-plot ((scatter xs ys) (scatter ys xs) "Data 2"))
等同于(同样在惯用的Python中)
fig, ax = plt.subplots()
ax.scatter(xs,ys)
ax.scatter(ys,xs)
ax.set_title("Data 2")
fig.savefig("Data 2")
但是以下朴素的尝试不起作用:
(defmacro/g! do-plot [main &optional title [fig-kwargs {}]]
`(do
(import [matplotlib.pyplot :as plt] [time [ctime]])
(setv [g!fig g!ax] (plt.subplots #**~fig-kwargs))
(do (lfor cmd ~main (insert-ax cmd)))
(when ~title (.set-title g!ax ~title))
(.savefig g!fig (if ~title ~title (ctime)))))
这将返回一个NameError: name 'scatter' is not definedNameError: name 'scatter' is not defined
。但这是可以理解的:在main
处理之前,我对insert-ax
的引用太早了。因此,下一个自然尝试:
现在我得到的错误是expanding macro do-plot NameError: name 'cmd' is not defined
。这可能是由于没有使用main
来使lfor循环/列表理解起作用的事实。因此,下一步是尝试取消引用整个循环:
(defmacro/g! do-plot [main &optional title [fig-kwargs {}]]
`(do
(import [matplotlib.pyplot :as plt] [time [ctime]])
(setv [g!fig g!ax] (plt.subplots #**~fig-kwargs))
(do ~(lfor cmd main (insert-ax cmd)))
(when ~title (.set-title g!ax ~title))
(.savefig g!fig (if ~title ~title (ctime)))))
然后我的下一个错误是expanding macro do-plot AttributeError: 'HySymbol' object has no attribute 'c'
。这似乎表明(因为AttributeError似乎与getattr
有关),~(first body))
的定义中的insert-ax
正在被评估为c
。
最后,出于一种狂喜的行为,我尝试了以下方法
(defmacro/g! do-plot [main &optional title [fig-kwargs {}]]
`(do
(import [matplotlib.pyplot :as plt] [time [ctime]])
(setv [g!fig g!ax] (plt.subplots #**~fig-kwargs))
(do ~@(lfor cmd main (insert-ax cmd)))
(when ~title (.set-title g!ax ~title))
(.savefig g!fig (if ~title ~title (ctime)))))
(尽管认为取消引号会融合我的表单)。这将静默失败,并且不产生任何输出。但是在这里hy2py返回相同的错误expanding macro do-plot AttributeError: 'HySymbol' object has no attribute 'c'
我还能尝试什么?
答案 0 :(得分:2)
宏的子例程通常比宏更好地编写为函数。函数使用起来更加灵活(它们是一流的对象),产生混乱的可能性较小,并且易于测试。这是我将insert-ax
作为函数来执行的操作:
(eval-and-compile (defn insert-ax [body]
`((. g!ax ~(first body)) ~@(rest body))))
(defmacro/g! do-plot [main &optional title [fig-kwargs {}]]
`(do
(import [matplotlib.pyplot :as plt] [time [ctime]])
(setv [g!fig g!ax] (plt.subplots #**~fig-kwargs))
~@(map insert-ax main)
(when ~title (.set-title g!ax ~title))
(.savefig g!fig (if ~title ~title (ctime)))))
请注意,eval-and-compile
(或eval-when-compile
)对于确保在扩展(do-plot …)
时在编译期间可用该功能是必需的。我也从内部进行了insert-ax
的简化,尽管这不是必需的。
此外,您在对do-plot
的呼叫中放错了括号。您想要的是:
(do-plot ((scatter xs ys) (scatter ys xs)) "Data 2")
为了完整起见,要使用原始的do-plot
来编写insert-ax
,请用~@(map insert-ax main)
替换上面的~@(lfor cmd main `(insert-ax ~cmd))
。