Racket 透明结构和预制结构之间有什么区别?

Racket 透明结构和预制结构之间有什么区别?,racket,Racket,正如标题所暗示的,我不理解在定义结构时使用#:transparent和使用#:prefact之间的区别。提到预制涉及某种全球共享 他们之间有什么区别?在哪种情况下我应该使用一种而不是另一种?对于预制结构可能有一个更温和的介绍 最大的区别是透明结构仍然需要结构构造函数来创建其中一个 例如,给定以下结构定义: (struct foo (a b) #:prefab) 以下是创建完全相同结构的两种方法 > (foo 1 2) '#s(foo 1 2) > #s(foo 1 2) '#s(f

正如标题所暗示的,我不理解在定义结构时使用
#:transparent
和使用
#:prefact
之间的区别。提到预制涉及某种全球共享

他们之间有什么区别?在哪种情况下我应该使用一种而不是另一种?

对于预制结构可能有一个更温和的介绍

最大的区别是透明结构仍然需要结构构造函数来创建其中一个

例如,给定以下结构定义:

(struct foo (a b) #:prefab)
以下是创建完全相同结构的两种方法

> (foo 1 2)
'#s(foo 1 2)
> #s(foo 1 2)
'#s(foo 1 2)
这意味着任何racket模块都可以创建一个
foo
预制结构,即使没有先定义它。如果您想将其放入宏中,或将其发送到另一台机器上运行的racket的单独实例中,这将非常有用


一般来说,我建议使用
#:transparent
结构,除非您需要
#:prefact
结构的全部功能。

要使结构类型透明,请在字段名序列后使用#:transparent关键字:

(struct posn (x y)
        #:transparent)

> (posn 1 2)
(posn 1 2)
透明结构类型的实例打印时类似于对构造函数的调用,因此它显示结构字段值。透明结构类型也允许反射操作,例如struct?和结构信息,用于其实例

尽管透明结构类型以显示其内容的方式打印,但与数字、字符串、符号或列表的打印形式不同,结构的打印形式不能在表达式中用于取回结构

预制(“先前制造的”)结构类型是一种内置类型,球拍打印机和表达式阅读器都知道该类型。存在无限多这样的类型,它们通过名称、字段计数、超类型和其他类似细节进行索引。预制结构的打印形式类似于向量,但它从#s开始,而不仅仅是#,打印形式中的第一个元素是预制结构类型的名称


最后,我认为你可能需要在大部分时间里使用#:transparent,根据我的经验,我通常使用#:transparent。

扩展其他答案,并为问题的第二部分提供更多示例:

#lang racket

(struct A (x y))
(displayln (A 1 2)) ; => #<A>
(equal? (A 1 2) (A 1 2)) ; => #f
;(equal? (A 1 2) (read (open-input-string (~a (A 1 2))))) ; => ERR: bad syntax

(struct B (x y) #:transparent)
(displayln (B 3 4)) ; => #(struct:B 3 4)
(equal? (B 3 4) (B 3 4)) ; => #t
(equal? (B 3 4) (read (open-input-string (~a (B 3 4))))) ; => #f

(struct C (x y) #:prefab)
(displayln (C 5 6)) ; => #s(C 5 6)
(equal? (C 5 6) (C 5 6)) ; => #t
(equal? (C 5 6) (read (open-input-string (~a (C 5 6))))) ; => #t
#朗球拍
(结构A(x y))
(displayln(a2));=>#
(相等)(A 12)(A 12));=>#F
;(等于?(A 1 2)(读取(打开输入字符串(~A(A 1 2)'));=>错误:语法错误
(结构B(x y)#:透明)
(displayln(b34));=>#(结构:B34)
(相等)(B34)(B34));=>#T
(等于?(B34)(读取(打开的输入字符串(~a(B34)'));=>#F
(结构C(x y)#:预制)
(displayln(c5-6));=>#s(C 5 6)
(等于?(C56)(C56));=>#T
(等于?(C56)(读取(打开的输入字符串(~a(C56)аа);=>#T
  • 如果您希望在没有例外的情况下强制执行结构抽象,即只能使用访问器创建和检查结构,请使用不透明结构
  • 使用透明结构授予对打印机和equal?的访问权限
  • 如果要进行序列化(例如从磁盘写入和读取),请使用预制结构

    • 还有一个重要细节尚未提及:透明(和正常)结构是生成的。这意味着,如果两次定义同一个结构,则使用结构定义的第一个实例创建的值与使用第二个定义创建的值不相等。您可以在REPL会话中亲自看到这一点:

      > (struct posn (x y) #:transparent)
      > (define origin1 (posn 0 0))
      > (struct posn (x y) #:transparent)
      > (define origin2 (posn 0 0))
      > (equal? origin1 origin2)
      #f
      
      尽管定义相同,内容相同,但这两个实例并不相等。尽管结构是透明的,但由于Leif Anderson指出的原因,它们被认为是独立的定义,仅仅使用#:transparent仍然要求创建结构的唯一方法是使用结构表单定义的构造函数。两个定义意味着两个不同的构造函数

      然而,对于预制结构,这种限制消失了——您可以自己创建预制结构,只需编写它们的读取器形式,如
      #s(posn0)
      。不再有理由要求使用定义的构造函数创建结构的所有实例,因此也没有理由两个不同但相同的结构定义不能相互识别:

      > (struct posn (x y) #:prefab)
      > (define origin1 (posn 0 0))
      > (struct posn (x y) #:prefab)
      > (define origin2 (posn 0 0))
      > (equal? origin1 origin2)
      #t
      > (equal? origin1 #s(posn 0 0))
      #t
      
      我的观点是,只表示汇集在一起的一些原始数据的结构应该是预制的,以获得自由、简单和安全的序列化,对如何构造它们有限制的结构应该是透明的,而应该封装一些行为和隐藏信息的结构既不应该是透明的,也不应该是预制的。这些仅仅是指导原则,但是,您的里程可能会有所不同。

      除了“奇异”反射式操作外,一个非常基本的东西只适用于
      \35;:透明的
      结构--
      相等?
      。给定的
      (结构x(y))
      (相等的?(x1)(x1))
      #f
      !在某种意义上,
      :transparent
      是介于不透明和预制之间的“温热粥碗”。事后看来,它应该是默认的,带有
      #:不透明的
      #:预制的
      修饰符,IMHO。这个答案可能应该是文本的来源。