Arrays Racket:宏输出奇怪的东西,而不是数组

Arrays Racket:宏输出奇怪的东西,而不是数组,arrays,macros,scheme,lisp,racket,Arrays,Macros,Scheme,Lisp,Racket,我的目标是在编译阶段(即在宏中)填充数组,并在执行阶段使用它。但由于某些原因,Racket无法将宏返回的对象识别为数组。为了说明问题,请使用显示此行为的最短代码: (require (for-syntax math/array)) (require math/array) (define-syntax (A stx) (datum->syntax stx `(define a ,(array #[#[1 2] #[3 4]])))) (A) 在执行这个宏之后,“a”是一个东西,但

我的目标是在编译阶段(即在宏中)填充数组,并在执行阶段使用它。但由于某些原因,Racket无法将宏返回的对象识别为数组。为了说明问题,请使用显示此行为的最短代码:

(require (for-syntax math/array))
(require math/array)

(define-syntax (A stx)
  (datum->syntax stx `(define a ,(array #[#[1 2] #[3 4]]))))

(A)
在执行这个宏之后,“a”是一个东西,但我不知道它是什么。它不是数组(
(array?a)->#f
)也不是字符串,array ref显然不处理它,但它打印为:
(array#[12]#[34])
。“诈骗”模块中的“类”声称它是“原语类:未知原语”,不管它值多少钱

我尝试输出一个向量而不是一个数组,但效果与预期一样,即结果值是执行阶段的向量

我曾尝试使用“兼容性”模块中的CommonLisp风格的defmacro,认为这可能与数据->语法转换有关,但这并没有改变什么

我已经在装有Racket 6.5和6.7的Win7上以及装有Racket 6.7的Linux上对此进行了测试-问题仍然存在

有什么想法吗


更新

多亏了大量的答案和建议,我想出了以下解决方案:

(require (for-syntax math/array))
(require math/array)

(define-syntax (my-array stx)
  (syntax-case stx ()
    [(_ id)
     (let
         ([arr (build-array
                #(20 20)
                (lambda (ind)
                  (let
                      ([x (vector-ref ind 1)]
                       [y (vector-ref ind 0)])
                    (list 'some-symbol x y (* x y)))))])
       (with-syntax ([syn-arr (eval (read (open-input-string (string-append "#'" (format "~v" arr)))))])
         #'(define id syn-arr)))]))

(my-array A)
我不确定这是否合适(我欢迎所有关于代码改进的建议),但下面是它的工作原理:

数组构建并存储在“arr”变量中。然后将其打印为字符串,并在前面加上
#“
(因此该字符串现在表示语法对象)并作为代码进行计算。这可以有效地将数组转换为语法对象,该对象可以嵌入到宏输出中


这种方法的优点是,每一个可以被写出来然后被Racket读回的对象都可以被宏输出。缺点是,有些对象不能(我在看你,自定义结构!),因此在某些情况下可能需要额外的字符串创建功能。

首先,不要像那样使用
datum->syntax
。你把所有的卫生信息都扔掉了,所以如果有人使用另一种语言,而
define
被称为其他语言(例如
def
),那就不起作用了。对于球拍宏的原则性介绍,请考虑阅读。

第二,这里的问题是,你正在创造一个有时被称为。在此上下文中,3D语法可能是一个错误,但要点是,只有一个语法对象可以安全地放入其中:

  • 象征
  • 布尔值
  • 人物
  • 一串
  • 空名单
  • 两条有效语法的一对
  • 有效语法的向量
  • 有效语法框
  • 有效语法键和值的哈希表
  • 包含唯一有效语法的预制结构
其他任何东西都是“3D语法”,作为宏的输出是非法的。值得注意的是,不允许使用
math/array
中的数组

这似乎是一个相当极端的限制,但关键是上面的列表只是一个可以在编译代码中结束的列表。Racket不知道如何将任意内容序列化为字节码,这是合理的:例如,在编译代码中嵌入闭包就没有多大意义。但是,生成一个表达式来创建一个数组是完全合理的,这是您在这里应该做的

更正确地编写宏,您将得到如下结果:

#lang racket

(require math/array)

(define-syntax (define-simple-array stx)
  (syntax-case stx ()
    [(_ id)
     #'(define id (array #(#(1 2) #(3 4))))]))

(define-simple-array x)

现在,
x
(数组[1 2][3 4]])
。请注意,您可以删除
数学/array
语法导入,因为您在编译时不再使用它,这很有意义:宏只处理代码位。在运行时,您只需要
math/array
就可以创建最终得到的实际值。

我怀疑OP希望Racket能够支持数组文本,这样它的使用就不会构成3D语法。我这样说是因为在Guile中,regexp不是简单的基准,因此在Guile宏中不能使用预编译的regexp而不创建3D语法,而Racket在没有3D语法的宏中直接支持regexp文本。@ChrisJester Young Yeah。可扩展文本目前是Racket中的一个公开问题(包括我在内的许多人都对此感兴趣,因为Racket允许以多种方式扩展语言)。目前提出的解决方案相对不令人满意,它们大多涉及完全避免
quote
,或使
quote
比目前复杂得多,破坏了
quote
的整体简洁性-但不清楚这是否可以在不解决许多棘手问题和执行大量工作的情况下以一般方式解决。回答得很好,谢谢!我对你的解决方案只有一个小问题。我知道,我可以将数组的计算让给执行阶段。在简化的例子中,这是完全可以接受和合理的。我试图做的是将数组计算移到宏中。换句话说,我知道在宏中我可以做(希望这是符合条件的):`(定义一个(构建数组#(20)(lambda(ind))),但在“构建数组”之前没有逗号,这就达不到目的了。我将不得不使用向量向量,然后(?)@LechNapierała您可以编写一个函数,将数组转换为表示生成相同数组的表达式的语法对象。也就是说,您可以编写一个函数,将
(array#[12])
转换为
(array#[12])
。不能对数组中的所有潜在值都这样做,但对于像数字这样的简单事情,这将相对简单。@LechNapierała如果可以简单地使用向量而不是数组的话。