在某些情况下,不变性会迫使您克隆对象并需要分配更多内存。它不需要占用内存,因为旧的副本可以被丢弃。例如,CLR 垃圾收集器可以很好地处理这种情况,所以这(通常)不是什么大问题。
但是,操作链实际上并不意味着克隆对象。功能列表当然就是这种情况。当您以典型方式使用它们时,您只需为单个元素分配一个内存单元(当将元素附加到列表的前面时)。
您的图像处理示例也可以以更有效的方式实现。我将使用 C# 语法来使代码易于理解,而无需了解任何 FP(但在通常的函数式语言中看起来会更好)。您可以只存储要对图像执行的操作,而不是实际克隆图像。例如这样的:
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
}
}
每次调用方法时,该类实际上不会创建整个位图的克隆。当您最终尝试保存图像时,它只会跟踪稍后需要完成的操作。在以下示例中,底层 Bitmap 将仅创建一次:
var i = new Image("file.jpg");
i.Resize(500, 800).Rotate(90).ConvertTo(Gif).SaveFile("fileNew.gif");
总之,代码看起来像是在克隆对象,而实际上每次调用某个操作时,您都在创建Image 类的新副本。但是,这并不意味着该操作占用大量内存 - 这可以隐藏在函数库中,可以通过各种方式实现(但仍保留重要的引用透明度)。