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

时间:2015-08-19 14:26:33

标签: racket

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

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

4 个答案:

答案 0 :(得分:8)

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

(struct posn (x y)
        #:transparent)

> (posn 1 2)
(posn 1 2)

透明结构类型的实例打印就像调用构造函数一样,以便显示结构字段值。透明结构类型还允许反射操作,例如struct?和struct-info,用于其实例。

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

预制(“先前制造”)结构类型是Racket打印机和表达式阅读器已知的内置类型。存在无限多个这样的类型,并且它们通过名称,字段计数,超类型和其他此类细节来索引。预制结构的印刷形式类似于矢量,但它以#s而不是#开头,而印刷形式的第一个元素是预制结构类型的名称。

最后,我认为您可能需要在#:prefab上使用#:透明,根据我的经验,我通常使用#:transparent。

答案 1 :(得分:6)

racket guide可能对预制结构有更温和的介绍。

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

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

IntegerProperty

以下是创建完全相同结构的两种方法。

ObjectProperty<Integer>

这意味着任何球拍模块都可以创建(struct foo (a b) #:prefab) 预制结构,即使没有先定义它。如果您想将其放在宏中,或者将其发送到在不同计算机上运行的单独的球拍实例,这将非常有用。

一般来说,我建议使用> (foo 1 2) '#s(foo 1 2) > #s(foo 1 2) '#s(foo 1 2) 结构,除非你需要foo结构的全部功能。

答案 2 :(得分:6)

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

#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
  • 如果要强制执行结构抽象而没有例外,请使用opaque结构,即只能使用访问器创建和检查结构。
  • 使用透明结构来访问打印机并且相同?。
  • 如果要进行序列化,请使用预制结构,例如:从磁盘写入和读取时。

答案 3 :(得分:3)

尚未提及的另一个重要细节:透明(和正常)结构生成。这意味着如果您定义两次相同的结构,则使用结构定义的第一个实例创建的值与使用第二个定义创建的值不是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

尽管定义相同且内容相同,但这两个实例不是equal?。虽然结构是透明的,但它们被认为是单独的定义,因为Leif Anderson指出,仅使用#:transparent仍然要求创建结构的唯一方法是使用结构形式定义的构造函数。两个定义意味着两个不同的构造函数。

但是,使用预制结构时,这种限制就会消失 - 你可以自己创建预制结构,只需编写它们的读者形式,如#s(posn 0 0)。不再有理由要求使用其定义的构造函数创建结构的所有实例,因此没有理由两个不同但相同的结构定义不会相互识别:

> (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

我认为,仅仅代表一些原始数据汇集在一起​​的结构应该是预制,以获得自由,简单和安全的序列化,限制它们如何构造的结构应该是透明的,结构应该封装一些行为和隐藏信息既不应透明也不应预制。这些仅仅是指导方针,但您的里程可能会有所不同。