Scheme R5RS标准中的6.3.6 Vectors部分说明了以下关于向量的内容:
向量是异构结构,其元素由整数索引。向量通常比相同长度的列表占用更少的空间,并且访问随机选择的元素所需的平均时间通常小于向量而不是列表。
这种向量的描述有点分散。
我想知道实际在vector-ref
和list-ref
操作及其复杂性方面意味着什么。两个过程都返回向量的第k个元素和列表。向量运算是O(1)并且是列表运算O(n)吗?矢量与列表有何不同?我在哪里可以找到更多相关信息?
现在我正在使用关联列表作为存储键/值对的数据结构,以便于查找。如果键是整数,那么使用向量来存储值可能会更好。
答案 0 :(得分:4)
vector-ref
和list-ref
的具体细节是依赖于实现的,这意味着:每个Scheme解释器都可以按照自己认为合适的方式实现规范,因此您的问题的答案不能推广到所有符合R5RS的口译员,它取决于您正在使用的实际口译员。
但是,是的,在任何体面的实现中都可以安全地假设vector-ref
操作是O(1),并且list-ref
操作可能是O(n)。为什么?因为引擎盖下的向量应该使用实现语言本身的数据结构来实现,这允许O(1)访问给定其索引的元素(例如,基本数组) - 因此实现{{1直截了当而Lisp中的列表是通过链接vector-ref
单元格创建的,并且在任何给定索引处查找元素需要遍历列表中的所有元素 - 因此O(n)复杂性。
作为旁注 - 是的,使用向量比使用键/值对的关联列表更快,只要键是整数并且预先知道要索引的元素的数量(Scheme向量可以它创建后不会增长它的大小)。对于一般情况(除整数之外的键,可变大小)检查您的解释器是否支持哈希表,或使用提供它们的外部库(例如,SRFI 69)。
答案 1 :(得分:1)
列表由cons
个单元格构成。来自R5RS list section:
列表的连续对的汽车字段中的对象是列表的元素。例如,一个双元素列表是一对,其car是第一个元素,其cdr是一对,其car是第二个元素,其cdr是空列表。列表的长度是元素的数量,与对的数量相同。
例如,列表(a b c)
等同于以下系列对:(a . (b . (c . ())))
并且可以通过以下“节点”在内存中表示:
[p] --> [p] --> [p] --> null
| | |
|==> a |==> b |==> c
每个节点[]
包含指向值p
的值car
,以及另一个指向下一个元素的指针(cdr
)。
这允许列表增长到无限长度,但需要ref
操作从列表的前面开始并遍历k
元素以便找到所请求的元素。如你所说,这是O(n)。
相比之下,向量基本上是一个值数组,可以在内部表示为指针数组。例如,向量#(a b c)
可能表示为:
[p p p]
| | |
| | |==> c
| |
| |==> b
|
|==> a
数组[]
包含一系列三个指针,并且每个指针都分配给向量中的值。因此,在内部,您可以使用符号v
引用向量v[3]
的第三个元素。由于您不需要遍历前面的元素,vector-ref
是一个O(1)操作。
主要缺点是矢量具有固定大小,因此如果需要添加比矢量可以容纳的更多元素,则必须分配新矢量并将旧元素复制到此新矢量。如果您的应用程序定期执行此操作,这可能是一项昂贵的操作。
网上有很多资源 - Scheme Data Structures上的这篇文章详细介绍并提供了一些示例,尽管它更侧重于列表。
所有这一切,如果您的密钥是(或可以成为)整数,并且您拥有固定数量的元素或者可以使用合理数量的向量重新分配进行管理 - 例如,您在启动时加载向量然后执行读取 - 向量可能是关联列表的有吸引力的替代。