更新2d计数表

时间:2012-09-26 21:48:38

标签: scala

假设我想要一个Scala数据结构,它实现了一个可以随时间变化的二维计数表(即,表中的各个单元可以递增或递减)。我该怎么做才能做到这一点?

我可以使用二维数组:

val x = Array.fill[Int](1, 2) = 0
x(1)(2) += 1

但是数组是可变的,我想我应该稍微偏好不可变数据结构。

所以我想到了使用二维矢量:

val x = Vector.fill[Int](1, 2) = 0
// how do I update this? I want to write something like val newX : Vector[Vector[Int]] = x.add((1, 2), 1)
// but I'm not sure how

但是我不确定如何只更换一个元素来获得新的矢量。

最好的方法是什么?

3 个答案:

答案 0 :(得分:5)

最好取决于您的标准。最简单的不可变变量是使用从(Int,Int)到您的计数的映射:

var c = (for (i <- 0 to 99; j <- 0 to 99) yield (i,j) -> 0).toMap

然后,您使用c(i,j)访问您的值并将其设置为c += ((i,j) -> n); c += ((i,j) -> (c(i,j)+1))有点烦人,但也不算太糟糕。

更快的是使用嵌套的Vector s - 大约2到3倍,这取决于你是否倾向于反复重新设置相同的元素 - 但它有一个丑陋的更新方法:

var v = Vector.fill(100,100)(0)
v(82)(49)     // Easy enough
v = v.updated(82, v(82).updated(49, v(82)(49)+1)    // Ouch!

更快(约2倍)只有一个你索引的矢量:

var u = Vector.fill(100*100)(0)
u(82*100 + 49)    // Um, you think I can always remember to do this right?
u = u.updated(82*100 + 49, u(82*100 + 49)+1)       // Well, that's actually better

如果您不需要不变性并且您的表格大小不会改变,那么只需使用您显示的数组。如果您所做的只是递增和递减整数,它比最快的矢量解决方案快〜200倍。

答案 1 :(得分:3)

如果您想以非常通用和功能性(但不一定是高性能)的方式进行此操作,则可以使用镜头。这是一个如何使用Scalaz 7实现的示例,例如:

import scalaz._

def at[A](i: Int): Lens[Seq[A], A] = Lens.lensg(a => a.updated(i, _), (_(i)))
def at2d[A](i: Int, j: Int) = at[Seq[A]](i) andThen at(j)

还有一点设置:

val table = Vector.tabulate(3, 4)(_ + _)

def show[A](t: Seq[Seq[A]]) = t.map(_ mkString " ") mkString "\n"

这给了我们:

scala> show(table)
res0: String = 
0 1 2 3
1 2 3 4
2 3 4 5

我们可以像这样使用我们的镜头:

scala> show(at2d(1, 2).set(table, 9))
res1: String = 
0 1 2 3
1 2 9 4
2 3 4 5

或者我们可以在给定的单元格中获取值:

scala> val v: Int = at2d(2, 3).get(table)
v: Int = 5

或者执行许多更复杂的操作,例如将函数应用于特定单元格:

scala> show(at2d(2, 2).mod(((_: Int) * 2), table))
res8: String = 
0 1 2 3
1 2 3 4
2 3 8 5

等等。

答案 2 :(得分:1)

没有内置的方法,可能是因为它需要Vector知道它包含向量,或向量或向量等,而大多数方法都是通用的,并且每个方法需要一个单独的方法维数,因为您需要为每个维指定一个坐标arg。

但是,您可以自己添加;以下内容将带您到4D,尽管如果您只需要,您可以添加2D位:

object UpdatableVector {
  implicit def vectorToUpdatableVector2[T](v: Vector[Vector[T]]) = new UpdatableVector2(v)
  implicit def vectorToUpdatableVector3[T](v: Vector[Vector[Vector[T]]]) = new UpdatableVector3(v)
  implicit def vectorToUpdatableVector4[T](v: Vector[Vector[Vector[Vector[T]]]]) = new UpdatableVector4(v)

  class UpdatableVector2[T](v: Vector[Vector[T]]) {
    def updated2(c1: Int, c2: Int)(newVal: T) =
      v.updated(c1, v(c1).updated(c2, newVal))
  }

  class UpdatableVector3[T](v: Vector[Vector[Vector[T]]]) {
    def updated3(c1: Int, c2: Int, c3: Int)(newVal: T) =
      v.updated(c1, v(c1).updated2(c2, c3)(newVal))
  }

  class UpdatableVector4[T](v: Vector[Vector[Vector[Vector[T]]]]) {
    def updated4(c1: Int, c2: Int, c3: Int, c4: Int)(newVal: T) =
      v.updated(c1, v(c1).updated3(c2, c3, c4)(newVal))
  }
}

在Scala 2.10中,您不需要隐式defs,只需将implicit关键字添加到类定义中即可。

测试:

  import UpdatableVector._

  val v2 = Vector.fill(2,2)(0)
  val r2 = v2.updated2(1,1)(42)
  println(r2) // Vector(Vector(0, 0), Vector(0, 42))

  val v3 = Vector.fill(2,2,2)(0)
  val r3 = v3.updated3(1,1,1)(42)
  println(r3) // etc

希望这很有用。