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,因为它们很长。
答案 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 ),无需后续转换。如果您比较List
和Vector
的实现,那么您会发现前置List
的操作比前置{{Vector
更简单,更便宜1}}。 List
完成后,Vector
可能必须在内部替换整个分支/子树,而不是仅在前面添加另一个元素。平均而言,这仍然会在恒定时间内发生("有效"常数,因为子树的大小可能不同),但比预先加List
更昂贵。从好的方面来说,您可以避免拨打toVector
。
最终,关键点是您要创建的集合的大小(换句话说,您正在执行的递归前置步骤的数量)。完全有可能没有明确的赢家,<= n
步骤的两个变体中的一个更快,而> n
步的另一个变体更快。在我天真的玩具基准测试中,List/toVecor
对于少于8k的元素似乎更快,但您应该执行一组精心选择的基准,以充分代表您的场景。