我的目标是在编译阶段(即在宏中)填充数组,并在执行阶段使用它。但由于某种原因,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 #[ #[1 2] #[3 4]])
。 “swindle”模块中的“class-of”声称它是“primitive-class:unknown-primitive”,就其值(value)而言。
我尝试输出向量而不是数组,但它按预期工作,即结果值是执行阶段的向量。
我尝试使用“兼容性”模块中的 CommonLisp 风格的 defmacro,认为这可能与数据->语法转换有关,但这没有改变任何东西。
我已经在 Win7 上使用 Racket 6.5 和 6.7 以及在 Linux 上使用 Racket 6.7 进行了测试 - 问题仍然存在。
有什么想法吗?
更新
感谢出色的回答和建议,我想出了以下解决方案:
(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)
我不确定这是否是合适的 Racket(我欢迎所有有关代码改进的建议),但它是如何工作的:
数组已构建并存储在“arr”变量中。然后将其打印为字符串,并在前面添加 #'
(以便该字符串现在代表语法对象)并作为代码进行计算。这有效地将数组转换为语法对象,可以嵌入到宏输出中。
这种方法的优点是,每个可以写出然后由 Racket 读回的对象都可以通过宏输出。缺点是,某些对象不能(我正在看着你,自定义结构!),因此在某些情况下可能需要额外的字符串创建函数。
最佳答案
首先,不要像那样使用datum->syntax
。你在那里扔掉了所有卫生信息,因此如果有人使用不同的语言,其中 define
被称为其他名称(例如 def
),那么这是行不通的。有关 Racket 宏的原则性介绍,请考虑阅读 Fear of Macros .
其次,这里的问题是您正在创建有时称为 “3D syntax” 的内容。 。在这种情况下,3D 语法可能应该是一个错误,但要点是只有 small set of things您可以安全地将其放入语法对象中:
- 一个符号
- 一个数字
- bool 值
- 一个字符
- 字符串
- 空列表
- 一对两段有效语法
- 有效语法向量
- 有效语法框
- 有效语法键和值的哈希表
- 包含唯一有效语法的预制结构
任何其他内容都是“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
是 (array #[#[1 2] #[3 4]])
。请注意,您可以删除 math/array 的 for-syntax 导入,因为您在编译时不再使用它,这是有道理的:宏只是操作代码位。您只需要在运行时使用 math/array
来创建最终得到的实际值。
关于arrays - Racket:宏输出一些奇怪的东西而不是数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41136717/