我在下面的代码中遇到了一个最令人不安的错误。即使我将它作为不可变的Map传递,按钮Map也会发生变异。键保持不变,并且地图指向两个不可变的Ints,但在下面您可以看到地图在运行期间明显具有不同的值。我绝对难过,不知道发生了什么。
def makeTrace(trace : List[(String)], buttons : Map[String, (Int,Int)],
outputScreen : ScreenRegion, hashMap : Map[Array[Byte], String])
: (List[(String,String)], Map[Array[Byte], String]) = {
println(buttons.toString)
//clearing the device
val clear = buttons.getOrElse("clear", throw new Exception("Clear Not Found"))
//clear.circle(3000)
val thisButton = new ScreenLocation(clear._1, clear._2)
click(thisButton)
//updates the map and returns a list of (transition, state)
trace.foldLeft((Nil : List[(String,String)], hashMap))( (list, trace) => {
println(buttons.toString)
val transition : String = trace
val location = buttons.getOrElse(transition, throw new Exception("whatever"))
val button = new ScreenLocation(location._1, location._2)
button.circle(500)
button.label(transition, 500)
click(button)
//reading and hashing
pause(500)
val capturedImage : BufferedImage = outputScreen.capture()
val outputStream : ByteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(capturedImage, "png", outputStream)
val byte : Array[Byte] = outputStream.toByteArray();
//end hash
//if our state exists for the hash
if (hashMap.contains(byte)){ list match {
case (accumulator, map) => ((transition , hashMap.getOrElse(byte, throw new Exception("Our map broke if"))):: accumulator, map)
}
//if we need to update the map
}else list match {
case (accumulator, map) => {
//adding a new state based on the maps size
val newMap : Map[Array[Byte], String] = map + ((byte , "State" + map.size.toString))
val imageFile : File = new File("State" + map.size.toString + ".png");
ImageIO.write(capturedImage, "png", imageFile);
((transition, newMap.getOrElse(byte, throw new Exception("Our map broke else"))) :: accumulator, newMap)
}
}
})
}
在我调用此函数之前,我将映射初始化为指向不可变对象的不可变映射。
val buttons = makeImmutable(MutButtons)
val traceAndMap = TraceFinder.makeTrace(("clear" ::"five"::"five"::"minus"::"five"::"equals":: Nil), buttons, outputScreen, Map.empty)
makeImmutable在哪里
def makeImmutable(buttons : Map[String, (Int,Int)]) : Map[String, (Int,Int)] = {
buttons.mapValues(button => button match {
case (x, y) =>
val newX = x
val newY = y
(newX,newY)
})
}
这是输出,你可以看到clear,minus和five
的状态变化Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (959,345), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (881,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,441), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (881,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
Map(equals -> (959,425), clear -> (842,313), minus -> (920,409), five -> (842,377))
答案 0 :(得分:2)
首先,尝试在代码周围添加println(map.getClass)
。确保它确实是您认为的地图。它的包名应该有immutable
,例如:
scala> println(Map(1->1, 2->2, 3->3, 4->4).getClass)
class scala.collection.immutable.Map$Map4
其次,确保你真的打印出你认为的完全相同的地图;使用引用标识哈希码是一种很好的方法:println(System.identityHashCode(map))
。
这些事情中的一个不会给你预期的结果,这是非常好的。然后你只需要弄清楚问题泄漏的地方。如果没有一个完整的可运行的例子,如果没有过多的代码盯着,很难提供更好的建议。
答案 1 :(得分:0)
我怀疑你在makeImmutable函数的范围内有scala.collection.Map的导入,它允许你将MutButtons(可能是一个可变的Map)传递给makeImmutable函数。 Map上的mapValues方法返回基础地图的视图,而不是不可变副本。例如:
import scala.collection.Map
import scala.collection.mutable.{Map => MutableMap}
def doubleValues(m: Map[Int, Int]) = m mapValues { _ * 2 }
val m = MutableMap(1 -> 1, 2 -> 2)
val n = doubleValues(m)
println("m = " + m)
println("n = " + n)
m += (3 -> 3)
println("n = " + n)
运行此程序会产生以下输出:
m = Map(2 -> 2, 1 -> 1)
n = Map(2 -> 4, 1 -> 2)
n = Map(2 -> 4, 1 -> 2, 3 -> 6)
要从makeImmutable返回一个真正不可变的映射,请在映射值后调用.toMap。