2010-05-20 78 views
12

为什么在每次通话中评估t.b?有没有办法让它只评估一次?F#记录成员评估

type test = 
    { a: float } 
    member x.b = 
    printfn "oh no" 
    x.a * 2. 

let t = { a = 1. } 
t.b 
t.b 
+0

令人失望的是,F#语言不支持不可变记录的一次计算值。我认为并发症是如果'a'被标记为可变的。 – Wally 2014-11-03 01:43:11

回答

12

这是一个属性;你基本上打电话给get_b()成员。

如果你想要的效果与构造,一旦发生,你可以使用一个类:

type Test(a:float) = 
    // constructor 
    let b = // compute it once, store it in a field in the class 
     printfn "oh no" 
     a * 2. 
    // properties 
    member this.A = a 
    member this.B = b 
+0

你是对的,但使用类我失去的东西就像c = {t with a = 4.},对吧? – 2010-05-20 12:55:29

+2

是的,但您可以使用可选参数编​​写构造函数,并获得非常相似的效果。 – Brian 2010-05-20 19:56:38

+1

我不明白你的想法。想象一下,我有一个带有10个参数的构造函数的Record,如{a:float; b:float,c:float ...}。从旧的创建一个新的记录完成{旧= c = 5}。如何在不重写构造函数中的所有参数的情况下对类进行相同操作? – 2010-05-21 10:55:10

14

Brian的回答的另一个版本,将最多一次评估b,但不会对其进行评估如果全部是B从未使用

type Test(a:float) = 
    // constructor 
    let b = lazy 
       printfn "oh no" 
       a * 2. 
    // properties 
    member this.A = a 
    member this.B = b.Value 
4

在回答您的意见Brian的帖子,你可以通过选配的名为/ ARGS假冒复制和更新记录表达式。例如:

type Person(?person:Person, ?name, ?age) = 

    let getExplicitOrCopiedArg arg argName copy = 
     match arg, person with 
     | Some(value), _ -> value 
     | None, Some(p) -> copy(p) 
     | None, None -> nullArg argName 

    let name = getExplicitOrCopiedArg name "name" (fun p -> p.Name) 
    let age = getExplicitOrCopiedArg age "age" (fun p -> p.Age) 

    member x.Name = name 
    member x.Age = age 

let bill = new Person(name = "Bill", age = 20) 
let olderBill = new Person(bill, age = 25) 

printfn "Name: %s, Age: %d" bill.Name bill.Age 
printfn "Name: %s, Age: %d" olderBill.Name olderBill.Age 
0

以前的答案建议切换到类而不是使用记录。如果你想留在记录(其简单的语法和不变性),你可以采取这种做法:

type test = 
    { a : float 
     b : float } 
    static member initialize (t: test) = 
     { t with b = t.a * 2. } 

如果是由其他库创建的test实例(如来自Web的数据提供者,这是有用服务或数据库)。使用这种方法,您必须记住在您的代码中使用它之前,通过初始化函数从该API接收到的任何test实例。