Racket:Macro输出奇怪的东西而不是数组

时间:2016-12-14 07:07:54

标签: arrays macros scheme lisp racket

我的目标是在编译阶段(即在宏中)填充数组,并在执行阶段使用它。但是,出于某种原因,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读回的每个对象。缺点是,某些对象不能(我正在看你,自定义结构!)因此在某些情况下可能需要额外的字符串创建功能。

1 个答案:

答案 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创建最终的实际值。