F # запись члена оценки - PullRequest
12 голосов
/ 20 мая 2010

Почему t.b оценивается при каждом вызове? И есть ли способ, как заставить его оценить только один раз?

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

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

Ответы [ 4 ]

15 голосов
/ 20 мая 2010

Альтернативная версия ответа Брайана, которая оценивает 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
13 голосов
/ 20 мая 2010

Это собственность;вы в основном вызываете элемент 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
4 голосов
/ 21 мая 2010

В ответ на ваши комментарии в сообщении Брайана вы можете подделывать выражения записи для копирования и обновления, используя дополнительные / именованные аргументы. Например:

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
1 голос
/ 03 ноября 2014

Предыдущие ответы предлагают переключиться на класс, вместо того, чтобы использовать запись. Если вы хотите остаться с записями (из-за их простого синтаксиса и неизменности), вы можете воспользоваться следующим подходом:

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

Это полезно, если экземпляр test создан другой библиотекой (например, поставщиком данных из веб-службы или базы данных). При таком подходе вы должны помнить, что любой экземпляр test, полученный вами от этого API, через функцию инициализации перед использованием его в вашем коде.

...