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
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
Đó 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
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? –
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
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? –
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
đá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
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.
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