2010-05-20 118 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實例。