【问题标题】:Immutable members on objects对象上的不可变成员
【发布时间】:2014-07-05 08:04:11
【问题描述】:

我有一个对象可以用一个有区别的联合来巧妙地描述。它所代表的树具有一些属性,这些属性可以在修改树时轻松更新(但保持不可变),但重新计算的成本相对较高。

我想将这些属性与对象一起存储为缓存值,但我不想将它们放入每个可区分的联合案例中,所以我认为一个成员变量适合这里。

那么问题是,如何在不改变实际对象的情况下更改成员值(当我修改树时)?我知道我可以修改树然后在不破坏纯度的情况下改变该副本,但这对我来说似乎是一种错误的方式。如果有一些预定义的方法来更改属性,但这样操作的结果是更改了该属性的新对象,这对我来说是有意义的。

澄清一下,当我说修改时,我的意思是以一种功能性的方式进行。像 (::) 一样“附加”到列表的开头。我不确定这里的正确术语是什么。

【问题讨论】:

  • 您能否给出一个有代表性的代码示例来说明您正在尝试做什么?从你的描述看来你想吃蛋糕也想吃蛋糕……
  • 由于我的实际用例有点复杂,我还在想办法,这里有一个简化的例子。假设您有一个分数,并且您决定将其表示为整数元组。现在,让我们假设计算分数的十进制表示非常昂贵。我可以有一个函数 addOne ,它只做 (x, y) -> (x + y, y) ,因为我知道 addOne 做什么,我也知道我只需要 (+1) 十进制表示。但是因为我的类型只是一个元组,没有地方缓存那个值,所以我每次都不得不重新计算,我不能使用 (+1) 快捷方式。
  • 因为我喜欢将分数作为元组而不是像 ((x, y), decimal) 这样的东西,所以我决定将缓存存储为成员变量。但是,如何实现我的 addOne 函数,使其不仅修改元组,还修改成员缓存?
  • 好的,那我给你一个代码示例,你告诉我我有多接近
  • 为什么不使用记录语法——看起来像let modified a = {a with b=c}

标签: f#


【解决方案1】:

F# 实际上有用于复制和更新记录的语法。

语法如下:

let myRecord3 = { myRecord2 with Y = 100; Z = 2 }

(来自 MSDN 记录页面的示例 - http://msdn.microsoft.com/en-us/library/dd233184.aspx)。

这允许记录类型是不可变的,并且大部分都被保留,而只有一小部分被更新。

【讨论】:

  • 语法是你要找的,但如果你想使用记录来存储你在给定时刻可能或可能没有的缓存值,请小心,因为它会影响结构相等比较你的类型。
  • 我明白了。这对我来说不是问题。
  • @JohnPalmer 您能否详细说明“大部分内容将被保留”?我猜这意味着所有的值都没有被复制,只是保存指针的结构,对吧?这意味着更新大记录中的一个字段比更新小记录中的一个字段要慢,对吧?
  • @LukaHorvat - 我相信你是正确的 - 但我没有测试过。
【解决方案2】:

最干净的方法实际上是以一种或另一种方式携带附加到 DU(作为案例的一部分)的“缓存”值。我可以想到几种方法来实现这一点,我只给你一种,缓存和非缓存模式有不同的情况:

type Fraction =
    | Frac of int * int
    | CachedFrac of (int * int) * decimal
    member this.AsFrac =
        match this with
        | Frac _ -> this
        | CachedFrac (tup, _) -> Frac tup

一个完全不同的选择是将缓存的值保存在一个单独的字典中,如果您只想节省一些时间重新计算它们,这是有意义的。

module FracCache = 
   let cache = System.Collections.Generic.Dictionary<Fraction, decimal>()

   let modify (oldFrac: Fraction) (newFrac: Fraction) =
       cache.[newFrac] <- cache.[oldFrac] + 1 // need to check if oldFrac has a cached value as well.

基本上 memoize 会给你带来什么,而且你可以更好地控制它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多