关于clojure:此宏有问题

Trouble with this macro

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

这是我写的宏:

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

它需要接受两个集合或序列,xsys,我需要它给我…

1
2
3
(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)作为我的第一个结果(我相信)。不幸的是,我尝试首先评估它们然后进行宏重写的所有操作都失败了/看起来非常骇人。

我确定我在这里缺少某种基本的语法-quote / unquote模式-我很乐意提供一些帮助/指针!

非常感谢。

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

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

编辑3我想我应该注意,我确实有一个宏版本正在工作,正如我上面提到的那样,它只是恐怖和骇人。它使用eval,如下所示,但是像这样:

1
2
3
4
(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的功能版本。

但是,我仍然对这个问题有一个更优雅,更朴实的宏解决方案持开放态度。如果存在!


我查看了画线条纹的宏扩展,发现它只是将主体包裹在一个绑定,gl-begin和gl-end中。因此,您可以将所需的任何代码放入其中。

所以

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

应该管用。


如果您确实需要draw-line-strip作为宏,并且想要一种完全通用的方法来执行问题文本所描述的内容,并且您不太在乎性能的提高,则可以使用eval

1
2
3
4
(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

请注意,除非确实必要,否则这是可怕的样式。


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

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

那应该用必需的参数调用draw-line-strip。此示例不是最适合宏的示例,不应在函数可以使用的地方使用它。

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

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

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


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

通常,宏使用它所包含的源并生成新的源。如果宏调用为(foo bar),并且宏应基于bar的值生成一些不同的东西,则通常是不可能的,因为bar的值通常不适用于编译器。 BAR实际上仅在运行时具有一个值,而在编译器扩展宏时则没有。因此,人们需要在运行时生成正确的代码-这可能是可行的,但通常被认为是不好的风格。

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

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

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

DRAW-LINE-STRIP是宏也不是一个好主意。最好将其编写为函数。