Macros 这个宏有问题吗

Macros 这个宏有问题吗,macros,clojure,lisp,Macros,Clojure,Lisp,令人尴尬的是,我在正确设计这个宏时遇到了一些问题 这是我写的宏: (defmacro construct-vertices [xs ys] (cons 'draw-line-strip (map #(list vertex %1 %2) xs ys))) 它需要包含两个集合或序列,xs和ys,我需要它给我 (draw-line-strip (vertex 0 1) (vertex 1 1) (vertex 3 3) (vertex 5

令人尴尬的是,我在正确设计这个宏时遇到了一些问题

这是我写的宏:

(defmacro construct-vertices
  [xs ys]
  (cons 'draw-line-strip
        (map #(list vertex %1 %2) xs ys)))
它需要包含两个集合或序列,
xs
ys
,我需要它给我

(draw-line-strip (vertex 0 1) (vertex 1 1) 
                 (vertex 3 3) (vertex 5 6) 
                 (vertex 7 8))
…对于
xs
=
[01357]
ys
=
[131368]

如果我给我的宏普通'n'简单向量(例如,
[1 2 3 4]
[2 3 4 5]
),这很好,但如果我给它一个惰性seq/任何需要像
那样计算的东西(取16(迭代(+0.1%1)0))
(取16(循环[0-0.1 0.1])

我意识到这是因为这些被传递给未赋值的宏,因此我得到了,例如,
(顶点take take)
作为我的第一个结果(我相信)。不幸的是,我试图先评估这些,然后执行宏重写的所有操作都失败了/看起来非常糟糕

我肯定我在这里缺少了一些基本的语法引号/取消引号模式——我想要一些帮助/指针

非常感谢


编辑我应该提到,
绘制线条条
是一个宏,
顶点
创建一个OpenGL顶点;他们都是这个世界的一部分

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

编辑3我想我应该注意到,我确实有一个宏版本在工作,就像我上面提到的那样,它非常可怕和粗糙。它使用
eval
,如下所示,但如下所示:

(defmacro construct-vertices
  [xs ys]
  (cons 'draw-line-strip
        (map #(list vertex %1 %2) (eval xs) (eval ys))))
不幸的是,我

错误:java.lang.ClassFormatError:在类文件tl/core$draw\u l$fn\u 9357(core.clj:14)的常量池中,该类索引3171无效。

…与数千项长列表一起使用时。这是因为我在预编译代码中写的太多了,而类文件无法处理(我想)那么多的数据/代码。看起来我需要,不知何故,获得一个功能版本的
绘制线条条
,正如建议的那样


然而,对于这个问题,我仍然对一个更加优雅、不那么粗俗的宏观解决方案持开放态度。如果有的话

为什么不这样做,用函数代替宏:

(defn construct-vertices [xs ys]
  (apply draw-line-strip (map #(list vertex %1 %2) xs ys)))
这将调用具有所需参数的绘制线带。这个例子不是最适合宏的,宏不应该用在函数可以做的地方

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

编辑:再次查看,我不知道在调用“绘制线带”之前是否要计算顶点。在这种情况下,函数将如下所示:

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

如果您确实需要
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

请注意,除非确实有必要,否则这种样式很糟糕。

这看起来像是Lisp中某些宏系统的典型问题。通常的Lisp文档适用。例如Paul Graham的(使用公共Lisp)

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

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

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


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

我查看了draw line strip的宏展开,注意到它只是将主体包裹在绑定、gl begin和gl end中。所以你可以在里面放任何你想要的代码

所以


应该可以工作。

为什么不写一个函数呢?因为据我所知,那是不可能的!我有这些
xs
ys
数组,从某种意义上说,我需要在
绘制线条
中重新格式化它们,并将它们包装在
顶点
中。我也对宏很感兴趣,尽管我非常乐意看到一个使用函数的解决方案。实际上,不仅可以将其作为函数编写(请参阅Dev er Dev的回答),而且实际上不可能将其作为宏编写并使用任意seq参数(考虑编译代码时必须完全确定宏的扩展).
draw lines strip
是一个宏,我应该提到。我不认为
apply
与宏一起工作。不,它没有,你应该将它编辑到问题中,因为这会改变一切。还要提到什么是
vertex
。还有一个问题:它真的需要是一个宏吗?它的名字明确地暗示了我们的工作一个函数…请注意,上述函数是一个函数,而不是宏,因此具有可用的运行时值
xs
ys
。然后使用这些值来构造一个
绘制线带
表单,该表单在运行时进行宏扩展和编译。这当然会带来一定的性能成本,但这确实是唯一的方法除了重写
绘制线条条
而不是宏(如果可能,这可能是最干净的解决方案)之外,继续回答问题要求.vertex不需要引用语法吗?编辑:啊,不,因为它在同一个ns中获取eval。我也会在这里发表评论:
绘制线条带
是一个宏,因此
应用
不能使用它。否则我会很高兴看到它
(apply (lambda (a b c)
          (a-macro-with-three-args a b c))
       list-of-three-elements)
(defn construct-vertices [xs ys]
  (draw-line-strip
    (dorun (map #(vertex %1 %2) xs ys))))