如何比较racket中的语法对象?

如何比较racket中的语法对象?,racket,Racket,我想比较两个语法对象的代码内容,忽略上下文之类的内容。将它们转换为数据是唯一的方法吗?比如: (equal? (syntax->datum #'(x+1)) (syntax->datum #'(x+1))) 如果您想比较两个对象而不解构它们,那么选择“是” 但是,该方法的问题在于,它只比较附加到两个语法对象的数据,而不会实际比较它们的绑定信息 我从中听到的类比是,这有点像拿两幅画,去掉它们的颜色,看看它们是否相同。虽然它们在某些方面可能相似,但您会错过不同颜色的许多差异 一种更好的

我想比较两个语法对象的代码内容,忽略上下文之类的内容。将它们转换为数据是唯一的方法吗?比如:

(equal? (syntax->datum #'(x+1)) (syntax->datum #'(x+1)))

如果您想比较两个对象而不解构它们,那么选择“是”

但是,该方法的问题在于,它只比较附加到两个语法对象的数据,而不会实际比较它们的绑定信息

我从中听到的类比是,这有点像拿两幅画,去掉它们的颜色,看看它们是否相同。虽然它们在某些方面可能相似,但您会错过不同颜色的许多差异

一种更好的方法(尽管它确实需要一些工作)是使用将语法对象分解为更原始的语法对象列表,并执行此操作,直到您从中获得(基本上是一个数据为符号的语法对象),您通常可以使用(有时还要查看每个标识符是否可以相互绑定,并比较模块级标识符

没有一个简单的谓词来比较两个任意语法对象的原因是,一般来说,没有一个很好的定义来说明是什么使两段代码相等,即使您只关心语法相等。例如,使用上面提到的函数不会跟踪语法对象中的内部绑定,因此,对于“相等”的含义,您仍然会得到一个非常严格的定义。也就是说,两个语法对象都具有相同的结构,标识符要么绑定到同一个模块,要么是
自由标识符=?


因此,在你使用这个答案之前,我强烈建议你退后一步,确保这是你真正想要做的。这是千载难逢的事,但大多数时候你实际上是在试图解决一个类似但更简单的问题。

这里有一个具体的例子,说明了一种可能的“更好的方法”莱夫·安德森提到

我已经在多个地方使用过它进行测试,不过如果有人想在非测试代码中使用它,他们可能会想重新访问一些设计决策

然而,无论您如何定义平等的含义,这里使用的模式都应该是有用的

您可能希望在以下方面做出不同选择:

  • 在标识符上,是要检查作用域是否完全相同(),还是要假设它们将绑定到语法对象之外,并检查它们是否绑定到相同的对象,即使它们具有不同的作用域()?请注意,如果选择第一个,则检查宏扩展的结果有时会返回
    #false
    ,因为范围不同,但如果选择第二个,则如果任何标识符未绑定到语法对象之外,则就好像您只关心名称上的
    符号=?
    相等,因此在一些不应该返回的地方,我会返回
    #true
    。我在这里选择了第一个,因为对于测试,测试失败的“假阳性”比测试不应该成功的“假阴性”要好

  • 在源位置上,您是要检查它们是否相等,还是要忽略它们?此代码忽略它们,因为它仅用于测试目的,但如果您只希望具有相同源位置的对象相等,则可能需要使用类似的函数来检查它们是否相等

  • 在语法属性上,是要检查它们是否相等,还是要忽略它们?此代码忽略它们,因为它仅用于测试目的,但如果要检查,则可能需要使用以下函数来检查它们

最后是代码。它可能不是您想要的,这取决于您如何回答上述问题。但是,它的结构和使用方式可能会对您有所帮助

(require rackunit)

;; Works on fully wrapped, non-wrapped, and partially
;; wrapped values, and it checks that the the inputs
;; are wrapped in all the same places. It checks scopes,
;; but it does not check source location.
(define-binary-check (check-stx=? stx=? actual expected))

;; Stx Stx -> Bool
(define (stx=? a b)
  (cond
    [(and (identifier? a) (identifier? b))
     (bound-identifier=? a b)]
    [(and (syntax? a) (syntax? b))
     (and (bound-identifier=? (datum->syntax a '||) (datum->syntax b '||))
          (stx=? (syntax-e a) (syntax-e b)))]
    [else
     (equal?/recur a b stx=?)]))