我的目标是在编译阶段(即在宏中)填充数组,并在执行阶段使用它。但是,出于某种原因,Rack返回的对象不能被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]])
。来自“诈骗”模块的“阶级”声称它是“原始阶级:未知 - 原始”,因为它的价值。
我尝试输出向量而不是数组,但它按预期工作,即结果值是执行阶段的向量。
我尝试使用“兼容性”模块中的CommonLisp样式defmacro,认为这可能与datum->语法转换有关,但这没有改变。
我在Win7上使用Racket 6.5和6.7进行了测试,以及在使用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)
我不确定这是否是合适的Racket(我欢迎所有关于代码改进的建议),但这是它的工作原理:
构建数组并存储在“arr”变量中。然后将其打印到字符串,前面加上#'
(以便此字符串现在表示语法对象)并作为代码进行评估。这有效地将数组转换为语法对象,可以嵌入宏输出中。
这种方法的优点是,可以通过宏输出可以写出然后由Racket读回的每个对象。缺点是,某些对象不能(我正在看你,自定义结构!)因此在某些情况下可能需要额外的字符串创建功能。
答案 0 :(得分:4)
首先,不要那样使用datum->syntax
。你丢弃了那里的所有卫生信息,所以如果有人使用的是另一种语言define
被称为其他语言(例如def
),那就不行了。有关Racket宏的原则介绍,请考虑阅读Fear of Macros。
其次,这里的问题是您正在创建有时称为“3D syntax”的内容。在这种情况下,3D语法应该是一个错误,但要点是只有small set of things可以安全地放在语法对象中:
其他任何是“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]])
。请注意,您可以删除for-syntax
math/array
的{{1}}导入,因为您在编译时不再使用它,这是有道理的:宏只是操纵一些代码。您只需在运行时math/array
创建最终的实际值。