功能上(即不可变地)更新Racket对象

时间:2016-03-10 00:13:42

标签: class object racket

假设我在Racket中定义了一个包含大约30个字段的类。

我想把它们视为不可变的。换句话说,我永远不会更新对象,只需创建一个新对象,其中29个字段包含相同的值,另一个具有覆盖的值。

是否有任何便利功能?

目前,我正在费力地编写像

这样的函数
(define/public (overwrite-n n-val) 
    (new MyClass% [a (get-a)] [b (get-b)] [c (get-c)] ... [n n-val] ... ))

我希望能够更新的每个字段。必须有更好的方法来做到这一点。

1 个答案:

答案 0 :(得分:3)

如果你想进行这样的功能更新,你可能最好使用结构,那里提供了struct-copygeneric interfaces等工具。

如果您确实需要使用racket/class,那么一个选项(如果这些是公共字段并且都使用init-field声明)使用反射机制和私有racket/class实现细节,如来自class-info的{​​{1}}和do-make-object。为了好玩,我制作了一个类似于racket/private/class-internal的{​​{1}}表单,这里有一个要点:object-copy.rkt

object-copy

另一种选择,如果您可以确定这些总是为真:

  • 您的班级没有任何初始化参数
  • 其子类不会
  • 在其他方法中,访问这些字段的唯一方法是使用getter方法

然后你可以覆盖getter方法来获得这种行为:

struct-copy

如果这些事情不对,或者你害怕有一天你会忘记并且他们会改变(很可能,特别是对于子类的事情),那么这会赢得&# 39;工作。如果#lang racket/base (require racket/class "object-copy.rkt") (define my-class% (class object% (super-new) (inspect #f) (init-field a b c) (define/public (get-a) a) (define/public (get-b) b) (define/public (get-c) c) (define/public (update-a a) (object-copy my-class% this [a a])) (define/public (update-b b) (object-copy my-class% this [b b])) (define/public (update-c c) (object-copy my-class% this [c c])) )) (define obj (new my-class% [a 1] [b 2] [c 3])) (send obj get-a) ; 1 (send (send obj update-a 4) get-a) ; 4 可以被子类化,那么使用#lang racket/base (require racket/class) (define my-class% (class object% (super-new) (define/public (get-a) (error 'a "uninitialized")) (define/public (get-b) (error 'b "uninitialized")) (define/public (get-c) (error 'c "uninitialized")) (define/public (update-a a) (new (class this% (super-new) (define/override (get-a) a)))) (define/public (update-b b) (new (class this% (super-new) (define/override (get-b) b)))) (define/public (update-c c) (new (class this% (super-new) (define/override (get-c) c)))) )) (define obj (send+ (new my-class%) (update-a 1) (update-b 2) (update-c 3))) (send obj get-a) ; 1 (send (send obj update-a 4) get-a) ; 4 并不是真正类型安全的。

虽然就像我在这个答案的顶部所说的那样,结构通常对这种类型的东西更好。