F#: let mutable vs. ref
(5)
Primero, reconozco la posibilidad de que esta pregunta sea un duplicado; sólo házmelo saber.
Tengo curiosidad por saber cuál es la "mejor práctica" general para aquellas situaciones en las que se desea la mutabilidad. F # parece ofrecer dos facilidades para esto: el enlace let mutable
, que parece funcionar como variables en "la mayoría de los idiomas", y la celda de referencia (creada con la función ref
) que requiere eliminación de referencias explícita para usar.
Hay un par de casos donde uno es "forzado" a una u otra: la interoperabilidad .NET tiende a usar mutable con <-
, y en los cálculos de flujo de trabajo se debe usar ref
con :=
. Entonces esos casos son bastante claros, pero tengo curiosidad por saber qué hacer cuando creo mis propias variables mutables fuera de esos escenarios. ¿Qué ventaja tiene un estilo sobre el otro? (Tal vez una mayor comprensión de la implementación ayude).
¡Gracias! (Y perdón por el "uso excesivo" de comillas).
Como se describe en este artículo de MSDN Blog en la sección Uso simplificado de valores variables , ya no necesita las celdas de ref para lambdas. Entonces, en general, ya no los necesitas.
Es posible que desee echar un vistazo a la sección Datos mutables en wikibook.
Para su comodidad, aquí hay algunas citas relevantes:
La palabra clave mutable se usa con frecuencia con tipos de registros para crear registros mutables
Las variables mutables son algo limitadas: las tablas mutables son inaccesibles fuera del alcance de la función donde están definidas. Específicamente, esto significa que no es posible hacer referencia a un mutable en una subfunción de otra función.
Las células Ref evitan algunas de las limitaciones de las mutables. De hecho, las células ref son tipos de datos muy simples que envuelven un campo mutable en un tipo de registro.
Como las células de referencia se asignan en el montón, se pueden compartir en múltiples funciones
Solo puedo respaldar lo que dijo Gradbot : cuando necesito una mutación, prefiero let mutable
.
Con respecto a la implementación y las diferencias entre las dos células ref
se implementan esencialmente mediante un registro muy simple que contiene un campo de registro mutable. Puedes escribirlos tú mismo fácilmente:
type ref<''T> = // ''
{ mutable value : ''T } // ''
// the ref function, ! and := operators look like this:
let (!) (a:ref<_>) = a.value
let (:=) (a:ref<_>) v = a.value <- v
let ref v = { value = v }
Una diferencia notable entre los dos enfoques es que let mutable
que let mutable
almacene el valor mutable en la pila (como una variable mutable en C #) mientras que ref
almacena el valor mutable en un campo de un registro asignado en el montón. Esto puede tener algún impacto en el rendimiento, pero no tengo ningún número ...
Gracias a esto, los valores mutables que usan ref
se pueden aliasar, lo que significa que puede crear dos valores que hagan referencia al mismo valor mutable:
let a = ref 5 // allocates a new record on the heap
let b = a // b references the same record
b := 10 // modifies the value of ''a'' as well!
let mutable a = 5 // mutable value on the stack
let mutable b = a // new mutable value initialized to current value of ''a''
b <- 10 // modifies the value of ''b'' only!
Este artículo de Brian podría proporcionar una respuesta.
Las mutables son fáciles de usar y eficientes (sin envoltorio), pero no se pueden capturar en lambdas. Las celdas de Ref se pueden capturar, pero son prolijas y menos eficientes (? - no estoy seguro de esto).
Pregunta relacionada: "Mencionó que los valores mutables locales no pueden ser capturados por un cierre, por lo que necesita usar ref. La razón para esto es que los valores mutables capturados en el cierre deben asignarse en el montón (porque el cierre se asigna en el montón) ". de campos F # ref-mutable vs object
Creo que let mutable
es preferible a las celdas de referencia. Personalmente, solo uso celdas de referencia cuando se requieren.
La mayoría del código que escribo no usa variables mutables gracias a la recursividad y las llamadas finales. Si tengo un grupo de datos mutables, uso un registro. Para los objetos que uso let mutable
para hacer variables privadas mutables. Solo uso celdas de referencia para cierres, generalmente eventos.