En algunos casos, la inmutabilidad obliga a clonar el objeto y necesita asignar más memoria. No es necesario ocupar la memoria, porque las copias más antiguas se pueden descartar. Por ejemplo, el recolector de basura de CLR trata bastante bien esta situación, por lo que esto no es (por lo general) un gran problema.
Sin embargo, encadenar operaciones no significa en realidad clonar el objeto. Este es ciertamente el caso de las listas funcionales. Cuando los usa de la manera típica, solo necesita asignar una celda de memoria para un solo elemento (al agregar elementos al principio de la lista).
Su ejemplo con procesamiento de imágenes también se puede implementar de una manera más eficiente. Usaré la sintaxis de C# para mantener el código fácil de entender sin conocer ningún FP (pero se vería mejor en un lenguaje funcional habitual). En lugar de clonar realmente la imagen, podría simplemente almacenar las operaciones que desea hacer con la imagen. Por ejemplo algo como esto:
class Image {
Bitmap source;
FileFormat format;
float newWidth, newHeight;
float rotation;
// Public constructor to load the image from a file
public Image(string sourceFile) {
this.source = Bitmap.FromFile(sourceFile);
this.newWidth = this.source.Width;
this.newHeight = this.source.Height;
}
// Private constructor used by the 'cloning' methods
private Image(Bitmap s, float w, float h, float r, FileFormat fmt) {
source = s; newWidth = w; newHeight = h;
rotation = r; format = fmt;
}
// Methods that can be used for creating modified clones of
// the 'Image' value using method chaining - these methods only
// store operations that we need to do later
public Image Rotate(float r) {
return new Image(source, newWidth, newHeight, rotation + r, format);
}
public Image Resize(float w, float h) {
return new Image(source, w, h, rotation, format);
}
public Image ConvertTo(FileFormat fmt) {
return new Image(source, newWidth, newHeight, rotation, fmt);
}
public void SaveFile(string f) {
// process all the operations here and save the image
}
}
La clase no crea realmente un clon de todo el mapa de bits cada vez que se invoca un método. Solo realiza un seguimiento de lo que se debe hacer más tarde, cuando finalmente intentarás guardar la imagen.En el siguiente ejemplo, el Bitmap
subyacente se crearía una sola vez:
var i = new Image("file.jpg");
i.Resize(500, 800).Rotate(90).ConvertTo(Gif).SaveFile("fileNew.gif");
En resumen, el código es el que está clonando el objeto y en realidad se está creando una nueva copia de la clase cada vez que Image
llamar a alguna operación. Sin embargo, eso no significa que la operación sea costosa en cuanto a la memoria: puede ocultarse en la biblioteca funcional, que puede implementarse de muchas maneras (pero preservar aún la importante transparencia referencial ).
creo que incluso un objeto con estado mutable necesitará tanto un buffer de origen como de destino al mutar una imagen para la mayoría de las transformaciones sustanciales, especialmente cosas como 'Resize()' y 'Rotate()'. – msandiford
Lo siento, no estoy seguro de entender la copia antigua, la nueva copia de pensamiento. ¿Es un objeto más grande para cada clase devuelto por los métodos encadenables, o es solo uno extra para el objeto individual ("i" en este caso). Si puede elaborar un poco, es muy apreciado. – ciscoheat
¿Cómo harías una transformación de espacio de color sin una imagen temporal completa? – Gabe