Martin Odersky为Scala Days 2013做了主题演讲。
一张标题为“When is an Object Mutable?”其内容如下:
class Memo[T, U](fn: T => U) {
val memo = new mutable.WeakHashMap[T, U]
def apply(x: T) = memo.getOrElseUpdate(x, fn(x))
}
// an object is mutable if its (functional) behavior depends on its history
new Memo {i: Int => i + 1} // immutable
var ctr = 0;
new Memo { i: Int => ctr += i; ctr } // mutable
请解释为什么备忘录示例分别是不可变和可变的。
答案 0 :(得分:3)
在第一个示例中,提供的函数将给定值增加1.结果不依赖于任何外部状态。
在第二个示例中,结果取决于ctr
值,该值是与Memo
相关的某种全局状态。换句话说,由ctr
值表示的全局状态可以影响第二个示例中的行为和结果。
答案 1 :(得分:0)
在某些情况下,即使某些内部使用的属性发生更改,但对象的状态从外部角度看似不变,也会将对象视为不可变。例如,使用memoization来缓存昂贵计算结果的对象仍然可以被视为不可变对象。
但是我想提请注意“可以 [...]被考虑”,所以不是每个人都认为它是不可变的。
如果您使用不可变Map来查看此Memo实现:
class Memo[T, U](val fn: T => U, map: Map[T, U]) {
def this(fn: T => U) {
this(fn, Map[T, U]())
}
def apply(x: T): (U, Memo[T, U]) = {
if (map.contains(x)) {
println("Cache hit")
(map(x), this)
} else {
println("Cache miss")
val result = fn(x)
val newMap = map + ((x, result))
(result, new Memo(fn, newMap))
}
}
}
根本没有可变的内部状态(它被委托给调用者)。但现在我们有两个不可变性程度不同的问题。
从实际角度来看,memoizer必须功能,但其名称/目的意味着可变性,即使它不影响外部行为。