【问题标题】:Macros that loop and transform a sequence of forms循环和转换一系列表单的宏
【发布时间】:2019-09-11 11:39:03
【问题描述】:

我正在编写宏来简化使用 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"))

which(在惯用的 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。但这是可以理解的:在insert-ax 处理之前,我太早取消引用main。于是下一个自然的尝试:

现在我得到的错误是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 有关)insert-ax 定义中的 ~(first body)) 正在被评估为 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'

我还能尝试什么?

【问题讨论】:

    标签: hy


    【解决方案1】:

    宏的子程序通常比宏写成函数更好。函数使用起来更灵活(它们是一流的对象),造成混淆的可能性更小,并且更容易测试。以下是我如何使用 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")
    

    为了完整起见,要使用原始insert-ax 宏编写do-plot,请将上面的~@(map insert-ax main) 替换为~@(lfor cmd main `(insert-ax ~cmd))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多