hy - 循环和转换表单序列的宏

标签 hy

我正在编写宏以简化使用 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 .

最后,出于 cargo 崇拜行为,我尝试了以下操作
(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'
我还能尝试什么?

最佳答案

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

关于hy - 循环和转换表单序列的宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57888339/

相关文章:

json - 如何将 hylang s-表达式序列化为 PostgreSQL json/json-b?

hy - if 和 cond 有什么区别?

hy - 在 Hy 中使用 let 进行动态绑定(bind)?

python - 在 Hy 中创建对象

hy - 为什么 '0 is false, but ' False 是true?

emacs - 在 Emacs 中为 Hy 使用 repl

python3/hy - 评估 python 中的 hy 表达式?

python - conda 托管环境中的 Shebang

hy - 如何在python中打印HyExpression?