我想要类似Pascal的变量索引数组,其中用户指定有效的索引范围。而不是将其内容索引的默认选择为0到length-1,我希望第一个索引是10,000,而最后一个索引是20,000。
Scala非常灵活,我想要的是可行的。但是,需要遵守规则来创建与其他集合协调的数据结构。
一个“明显”的解决方案可能是简单地将大小指定为20,000并浪费前半部分内存,但这并不好。此外,可以使地图结构快速工作。但是HashMap所涉及的开销是一个数组级结构的数量级,如我所描述的。
理想情况下,它不应该重复或重新发明已有的代码,也不应该过于复杂,甚至内部。
作为我想要的一个例子,考虑一个具有从500到999的有效索引的数组。我将我的类称为IndexedArray,以区别于Scala中使用的标准数组。此外,我将使用Scala的传统范围来指定声明中的索引范围。
为了简单起见,我不需要我的数据结构有一个不同的步幅。所有索引都是连续的。
var histogram = new IndexedArray[Int](500 to 999)
histogram(500) = 10 // "first" entry has value 10
histogram(999) += 1 // "last" entry has value 1
当然,只需使用标准的Scala数组,并通过每次访问重新映射索引,就可以解决问题。但这不是错误的秘诀吗?我希望数据结构隐藏我眼中的细节。
我的数据结构当然是可变的,因为Scala中的标准数组是可变的;并且行为的唯一区别是索引将在每次访问时自动重新映射。
答案 0 :(得分:0)
事实证明,至少得到一个现在像宣传的那样有效的基本结构并不困难。我真的更希望我的类扩展Scala集合中已有的适当结构,以便与其他可变集合API无形地合并。但是到目前为止我还没能做到这一点。
然而,我的基本解决方案通过使用与标准Scala数组相同的语法来显示Scala的强大功能 。
class IndexedArray[T: ClassTag](val range: Range) {
val size: Int = range.end - range.start + 1
val start: Int = range.start
val array = new Array[T](size)
def apply(index: Int): T = array(index - start).asInstanceOf[T]
lazy val elemTag: ClassTag[T] = ClassTag[T](array.getClass.getComponentType)
def length: Int = array.length
def update(index: Int, elem: T): Unit = array.update( index - start, elem )
}
现在可以使用上述语法实例化数据结构,并使用如下的数据结构。 (对于Scala新手,以下两行是等效的,并且做同样的事情。)
var pascalLike = IndexedArray[Int](500 until 1000)
或
var pascalLike = IndexedArray[Int](500 to 999)
然后,我们可以使用该结构。
pascalLike(500) = 100 // Sets the first element to 100
pascalLike(500) += 1 // Adds 1 to the first element
pascalLike(501) -= 2 // Subtracts 2 from the second element
println(pascalLike(501)) // Prints "-2"
我不知道如何制作这种结构" flow"与Scala集合API的其余部分一起使用。例如,我希望zipWithIndex
索引方法能够像人们对这样的数据结构所期望的那样工作,使用适合于结构的索引。
现在,合并这个人的唯一方法就是抓住底层的array
成员并希望Scala编译器允许隐式转换与用户想要做的事情相协调,这并不是很好符合我原来的目标。