使用List + .toVector或Vector进行尾递归?

时间:2016-11-04 08:53:50

标签: scala

  val dimensionality = 10
  val zeros = DenseVector.zeros[Double](dimensionality)

  @tailrec private def specials(list: List[DenseVector[Int]], i: Int): List[DenseVector[Int]] = {
    if(i >= dimensionality) list
    else {
      val vec = zeros.copy
      vec(i to i) := 1
      specials(vec :: list, i + 1)
    }
  }

  val specialList = specials(Nil, 0).toVector
  specialList.map(...doing my thing...)

我应该使用上面的List作为累加器来编写我的尾递归函数,然后编写

specials(Nil, 0).toVector

或者我应该首先使用Vector编写我的跟踪递归?什么在计算上更有效?

顺便说一下:specialList是一个包含DenseVectors的列表,其中每个条目都是0,但一个条目除外,它是1.有很多DenseVectors,因为它们很长。

2 个答案:

答案 0 :(得分:1)

我不是你想要在这里做什么,但你可以像这样重写你的代码:

type Mat = List[Vector[Int]]

@tailrec
private def specials(mat: Mat, i: Int): Mat = i match {
  case `dimensionality` => mat
  case _ => 
    val v = zeros.copy.updated(i,1)
    specials(v :: mat, i + 1)
}

在处理矩阵时,Vector可能是更好的选择。

答案 1 :(得分:1)

让我们比较两种变体的性能特征:

  • List:前置需要一段时间,转换为Vector需要线性时间。
  • Vector:prepend take"有效"恒定时间( eC ),无需后续转换。

如果您比较ListVector的实现,那么您会发现前置List的操作比前置{{Vector更简单,更便宜1}}。 List完成后,Vector可能必须在内部替换整个分支/子树,而不是仅在前面添加另一个元素。平均而言,这仍然会在恒定时间内发生("有效"常数,因为子树的大小可能不同),但比预先加List更昂贵。从好的方面来说,您可以避免拨打toVector

最终,关键点是您要创建的集合的大小(换句话说,您正在执行的递归前置步骤的数量)。完全有可能没有明确的赢家,<= n步骤的两个变体中的一个更快,而> n步的另一个变体更快。在我天真的玩具基准测试中,List/toVecor 对于少于8k的元素似乎更快,但您应该执行一组精心选择的基准,以充分代表您的场景。