¿Por qué se evalúa t.b en cada llamada? ¿Y hay alguna forma de hacer que se evalúe solo una vez?F # evaluación de miembro de registro
type test =
{ a: float }
member x.b =
printfn "oh no"
x.a * 2.
let t = { a = 1. }
t.b
t.b
¿Por qué se evalúa t.b en cada llamada? ¿Y hay alguna forma de hacer que se evalúe solo una vez?F # evaluación de miembro de registro
type test =
{ a: float }
member x.b =
printfn "oh no"
x.a * 2.
let t = { a = 1. }
t.b
t.b
Es una propiedad; Básicamente estás llamando al miembro get_b()
.
Si desea que el efecto ocurra una vez con el constructor, se puede usar una clase:
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
Tienes razón, pero usando clases pierdo cosas como let c = {t con a = 4.}, ¿verdad? –
Sí, pero puede escribir un constructor con parámetros opcionales y obtener un efecto muy similar. – Brian
No entiendo tu idea. Imagine que tengo un Record con un constructor que tiene 10 parámetros como {a: float; b: float, c: float ...}. Crear un nuevo registro de uno antiguo se hace como {antiguo con c = 5}. ¿Cómo hago lo mismo con las clases sin reescribir todos los parámetros en el constructor? –
Una versión alternativa de la respuesta de Brian que evaluará b
como máximo una vez, pero no lo evaluará en todos si no se usa nunca B
type Test(a:float) =
// constructor
let b = lazy
printfn "oh no"
a * 2.
// properties
member this.A = a
member this.B = b.Value
en respuesta a sus comentarios en el post de Brian, que se puede fingir récord de copiar y actualización de las expresiones que utilizan argumentos opcionales/con nombre. Por ejemplo:
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
Las respuestas anteriores sugieren cambiar a una clase, en lugar de usar un registro. Si desea quedarse con los registros (por su sencilla sintaxis y la inmutabilidad), puede tomar este enfoque:
type test =
{ a : float
b : float }
static member initialize (t: test) =
{ t with b = t.a * 2. }
Esto es útil si la instancia de test
es creado por otra biblioteca (como un proveedor de datos de una web servicio o base de datos). Con este enfoque, debe recordar pasar cualquier instancia de test
que reciba de esa API a través de la función de inicialización antes de usarla en su código.
Es decepcionante que el lenguaje F # no sea compatible con los valores calculados de una sola vez para los registros inmutables. Supongo que la complicación es si 'a' está marcado como mutable. – Wally