Debugging 为什么我的不可变对象会改变Scala

Debugging 为什么我的不可变对象会改变Scala,debugging,scala,immutability,Debugging,Scala,Immutability,我在下面的代码中遇到了一个最麻烦的错误。按钮映射会发生变化,即使我将其作为不可变映射传递。键保持不变,映射指向两个不可变的int,但在下面您可以清楚地看到映射在运行期间具有不同的值。我完全被难住了,不知道发生了什么事 def makeTrace(trace : List[(String)], buttons : Map[String, (Int,Int)], outputScreen : ScreenRegion, hashMap : Map[Array[Byte], String])

我在下面的代码中遇到了一个最麻烦的错误。按钮映射会发生变化,即使我将其作为不可变映射传递。键保持不变,映射指向两个不可变的int,但在下面您可以清楚地看到映射在运行期间具有不同的值。我完全被难住了,不知道发生了什么事

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、减号和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))

我怀疑您在makeImmutable函数的作用域中导入了scala.collection.Map,它允许您将MutButtons(可能是一个可变映射)传递给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。

首先,尝试在代码周围喷洒
println(map.getClass)
。确保它真的是你认为的地图。它的包名中应该有
不可变的
,例如:

scala> println(Map(1->1, 2->2, 3->3, 4->4).getClass)
class scala.collection.immutable.Map$Map4
第二,确保你真的打印出了与你想象中完全相同的地图;使用参考标识哈希代码是一种很好的方法:
println(System.identityHashCode(map))


这些事情中有一件很可能不会给你带来预期的结果。然后,你只需要找出问题是从哪里漏出来的。如果没有一个完整的可运行的示例,就很难在没有大量代码的情况下提供更好的建议。

要么映射必须发生变化,要么映射中的对象必须发生变化。没有明智的选择。找出哪个-慢慢删除所有不必要的代码,这样问题仍然存在。(嗯,我想可能是某个地方打印的另一张地图…)我不知道格式,但我会在上面插入它。在调用函数之前,我要做的就是创建一个包含不可变对象的不可变映射。我将发布上面的代码。谢谢你的帮助,但我恐怕这不是它。我从不声明可变映射,我的makeImmutable函数就是为了向自己证明我不是疯子。我添加了.toMap只是为了确定,我仍然有变异的问题。啊,糟糕。我曾经被mapValues以这种方式咬过,所以我想这可能是原因。这是非常棒的建议,谢谢!我解决了我的问题,也解决了scala的一些问题。当您使用mapValues函数时,scala似乎围绕着用于映射的函数而不是值!我将我的地图转换成一个列表,然后又重新转换成一个地图,这似乎解决了它!编辑:我不够冷静,不能给你投票,很抱歉,很高兴你把它修好了。另一种严格映射值的方法是这样的:
mmap{case(k,v)=>(k,f(v))}
。这会立即构建一张新地图。@dakillakan这也是我几天前问的问题。人们提供了从
mapValues
生成新的
Map
的其他可能性,即使用
force