Scala 更新二维计数表

Scala 更新二维计数表,scala,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 somethi

假设我想要一个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
但我不知道如何得到一个只改变一个元素的新向量


最佳方法是什么?

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

var c = (for (i <- 0 to 99; j <- 0 to 99) yield (i,j) -> 0).toMap
更快的是(大约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倍。

如果你想以一种非常通用和实用(但不一定是性能)的方式来实现这一点,你可以使用镜头。下面是一个如何使用的实现的示例,例如:

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

依此类推。

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

但是,您可以自己添加这些;以下内容将占用您最多4D的空间,但如果您只需要添加2D的位,您也可以添加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中,您不需要隐式定义,只需将
implicit
关键字添加到类定义中即可

测试:


希望这是有用的。

vector的
。更新
方法要在有效的常量中完成time@om-nom nom-尽管与更改数组中的基元相比,常数因子非常非常大。如果你只想一遍又一遍地更新矩阵中的一个随机单元,那么我手中的100x100
数组大约是500倍。看起来是这样的@om nom nom-不,他说函数不是自动变好的。这表明不可变并不是自动地更好。通常这两者是密切相关的,但它们不是一回事。
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))
  }
}
  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