麻烦这个宏

时间:2010-07-28 20:40:24

标签: macros clojure lisp

令人尴尬的是,我在设计这个宏时遇到了一些麻烦。

这是我编写的宏:

(defmacro construct-vertices
  [xs ys]
  (cons 'draw-line-strip
        (map #(list vertex %1 %2) xs ys)))

它需要接收两个集合或seq,xsys,我需要它来给我......

(draw-line-strip (vertex 0 1) (vertex 1 1) 
                 (vertex 3 3) (vertex 5 6) 
                 (vertex 7 8))

...代表xs = [0 1 3 5 7]ys = [1 1 3 6 8]

如果我给我的宏简单'n'简单向量(例如[1 2 3 4][2 3 4 5]),这可以正常工作但是如果我给它一个lazy-seq /需要的任何东西它不起作用评估方式如(take 16 (iterate #(+ 0.1 %1) 0))(take 16 (cycle [0 -0.1 0 0.1]))))

我意识到这是因为这些被传递给未经评估的宏,所以我得到了(vertex take take)作为我的第一个结果(我相信)。不幸的是,我试图首先评估这些然后执行我的宏重写的所有内容都失败了/看起来非常黑客。

我确定我在这里缺少某种基本的语法 - 引用/取消引用模式 - 我会喜欢一些帮助/指针!

非常感谢。

编辑我应该提一下,draw-line-strip是一个宏,vertex创建一个OpenGL顶点;它们都是Penumbra Clojure+OpenGL library的一部分。

编辑2 这是我需要的自定义图形工具,创建它的主要动机是比JFreeCharts和公司更快。

编辑3 我想我应该注意到我有一个宏版本工作,它就像我上面提到的那样可怕而且很黑。它使用eval,如下所示,但是像这样:

(defmacro construct-vertices
  [xs ys]
  (cons 'draw-line-strip
        (map #(list vertex %1 %2) (eval xs) (eval ys))))

不幸的是,我得到......

error: java.lang.ClassFormatError: Invalid this class index 3171 in constant pool in class file tl/core$draw_l$fn__9357 (core.clj:14)

...当使用这个带有几千个项目的长列表时。这是因为我在预编译的代码中写得太多了,而且类文件无法处理(我想)那么多的数据/代码。看起来我需要以某种方式获得draw-line-strip的函数版本,正如所建议的那样。

然而,对于这个问题,我仍然是一个更优雅,更少黑客的宏观解决方案。如果存在!

4 个答案:

答案 0 :(得分:4)

我查看了绘制线条的宏扩展,发现它只是将主体包裹在绑定,gl-begin和gl-end中。所以你可以把你想要的任何代码放在里面。

所以

(defn construct-vertices [xs ys]
  (draw-line-strip
    (dorun (map #(vertex %1 %2) xs ys))))

应该有用。

答案 1 :(得分:2)

为什么不是这样的,使用函数而不是宏:

(defn construct-vertices [xs ys]
  (apply draw-line-strip (map #(list vertex %1 %2) xs ys)))

应该使用必需的args调用draw-line-strip。这个例子不适合宏,不应该在函数可以使用的地方使用。

注意:我没有尝试过,因为我没有在这个盒子上设置粘液。

编辑:再看一下,我不知道你是否想在调用draw-line-strip之前评估顶点。在这种情况下,函数将如下所示:

(defn construct-vertices [xs ys]
  (apply draw-line-strip (map #(vertex %1 %2) xs ys)))

答案 2 :(得分:2)

如果你真的需要draw-line-strip成为一个宏,你需要一个完全通用的方法来处理问题文本描述的你不在乎关于性能损失太多,您可以使用eval

(defn construct-vertices [xs ys]
  (eval `(draw-line-strip ~@(map #(list 'vertex %1 %2) xs ys))))
                                      ; ^- not sure what vertex is
                                      ;    and thus whether you need this quote

请注意,这是非常糟糕的风格,除非真的必要。

答案 3 :(得分:1)

这看起来像是Lisp中一些宏系统的典型问题。通常的Lisp文献适用。例如Paul Graham的 On Lisp (使用Common Lisp)。

通常,宏使用它所包含的源并生成新的源。如果宏调用是(foo bar),并且宏应该根据bar的值生成不同的东西,那么这通常是不可能的,因为bar的值通常不可用于编译器。 BAR在运行时确实只有一个值,而不是在编译器扩展宏时。因此,人们需要在运行时生成正确的代码 - 这可能是可能的,但通常被认为是糟糕的风格。

在这些宏系统中,无法应用宏。典型的解决方案如下所示(Common Lisp):

(apply (lambda (a b c)
          (a-macro-with-three-args a b c))
       list-of-three-elements)

但是,并不总是可以使用上述解决方案。例如,当参数的数量变化时。

DRAW-LINE-STRIP是一个宏也不是一个好主意。最好把它写成一个函数。