2010-05-20 39 views
12

Tại sao t.b được đánh giá trên mọi cuộc gọi? Và có cách nào để làm cho nó đánh giá chỉ một lần?Đánh giá thành viên F # bản ghi

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

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

Thật đáng thất vọng khi ngôn ngữ F # không hỗ trợ giá trị được tính một lần cho các bản ghi bất biến. Tôi cho rằng sự phức tạp là nếu 'a' được gắn cờ là có thể thay đổi được. – Wally

Trả lời

12

Đó là tài sản; về cơ bản bạn đang gọi thành viên get_b().

Nếu bạn muốn tác động để xảy ra một lần với các nhà xây dựng, bạn có thể sử dụng một lớp:

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

Bạn nói đúng, nhưng sử dụng các lớp tôi mất những thứ như cho phép c = {t với a = 4.}, phải không? –

+2

Có, nhưng bạn có thể viết một hàm tạo với các tham số tùy chọn và nhận được một hiệu ứng rất giống nhau. – Brian

+1

Tôi không hiểu ý tưởng của bạn. Hãy tưởng tượng rằng tôi có một Record với hàm tạo có 10 tham số như {a: float; b: float, c: float ...}. Tạo bản ghi mới từ bản cũ được thực hiện dưới dạng {old with c = 5}. Làm thế nào để làm tương tự với các lớp học mà không cần viết lại tất cả các tham số trong constructor? –

14

Một phiên bản khác của câu trả lời của Brian rằng sẽ đánh giá b cùng một lúc nhất, nhưng sẽ không đánh giá nó ở tất cả nếu B không bao giờ được sử dụng

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

đáp lại bình luận của bạn trong bài viết của Brian, bạn có thể giả mạo biểu thức sao chép và cập nhật bản ghi sử dụng tùy chọn/tên args. Ví dụ:

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

Các câu trả lời trước đề xuất chuyển sang lớp học, thay vì sử dụng bản ghi. Nếu bạn muốn ở lại với các hồ sơ (đối với cú pháp đơn giản và không thay đổi), bạn có thể áp dụng cách này:

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

này rất hữu ích nếu trường hợp của test được tạo ra bởi thư viện khác (như một nhà cung cấp dữ liệu từ một trang web dịch vụ hoặc cơ sở dữ liệu). Với cách tiếp cận này, bạn phải nhớ truyền bất kỳ cá thể nào của test mà bạn nhận được từ API đó thông qua hàm khởi tạo trước khi sử dụng nó trong mã của bạn.

Các vấn đề liên quan