arrays - 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'是个东西,但我不知道它是什么。它不是一个数组 ((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/

相关文章:

scheme - 返回所有其他元素列表的方案过程

c++ - 如何调整属于某个类的唯一指针的 char 数组的大小。它必须在程序的整个生命周期中保持事件状态

c++ - 将 argv 与函数一起使用时出错

macros - 从 Clojure 中的宏返回宏

lisp - 有人在商业上使用 Racket 吗?

scheme - Lisp中最长的元素链

javascript - 在不使用嵌套 for 循环的情况下迭代 JavaScript 数组

C 二维数组段错误

用于整数列表的 Ruby 百分比宏(a la %q, %x, %w)?

objective-c - 如何制作 NSString 宏?