许多编程语言提供内置数据结构,但已知某些数据结构(例如链表)非常易于实现,因此,语言通常不包括内置数据结构。
某些语言,例如C++ has(作为std::list
,双重链接),as well as Java(作为LinkedList<T>
,双重链接)。
例如,Python已经在其库中内置了几个数据结构,包括列表,元组,集合,字典(甚至不需要导入),以及collections
库中的数据结构。它没有内置的LinkedList
数据结构,因为在Python中已经很容易实现自己的LinkedList。
请注意,"What is the underlying data structure for Python lists?"中回答的Python列表的基础数据结构实际上是数组。此外,collections
中的 deque 是一个双端队列,它应该具有缺少的功能,因为它通常不支持索引或插入中间(但是,自index
和insert
以来,索引功能已添加到3.5,但在Python 2中不存在。
似乎提供内置LinkedList
数据结构是多余的,不必要的。为什么其他一些编程语言库(如C ++和Java库)包含LinkedList
数据结构,即使它们易于实现?如果是这样,用这些语言实现自己的链表有什么风险呢?
答案 0 :(得分:7)
我搜索了Guido的Python History博客,因为我确信他已经写过这个,但显然这不是他这样做的地方。所以,这是基于推理(又名有根据的猜测)和记忆(可能有缺陷)的组合。
让我们从最后开始:不知道为什么Guido没有在Python 0.x中添加链表,我们至少知道为什么核心开发人员从那以后没有添加它们,即使他们已经添加了一堆其他类型的OrderedDict
到set
?
是的,我们这样做。简短的版本是:二十多年来没有人要求它。几年来内置或标准库中添加的内容几乎都是(一种变体)在PyPI或ActiveState recipes上被证明有用和流行的东西。例如,OrderedDict
和defaultdict
来自enum
和dataclass
(基于attrs
)。有一些流行的库用于其他一些容器类型 - 排序的字典/集合,OrderedSet,树和尝试等的各种排列,并且SortedContainers
和blist
已被提出,但被拒绝,包含在stdlib。
但是没有流行的链表库,这就是为什么它们永远不会被添加。
因此,这使问题回到了一步:为什么没有流行的链表库?
首先,“链表”实际上并不是一种类型,而是一整套类型 - 单一与双链,节点列表与句柄列表,尾部共享与否,懒惰(推测)通过@property
- 样式触发器)或严格......以及像圆形列表这样的变体。当然,这些中的每一个都远远不如整个家庭。并且你不能在它们的接口或实现之间共享那么多 - 用于处理Lisp风格的cons
列表的接口对于像C ++ std::list
这样的东西毫无意义。
其次,所有不同的变化都很容易构建。 OrderedDict
使其成为语言的原因是人们能够证明实现这一目标非常棘手。集合很容易正确,但如果不借用幕后的实现细节,就不可能进行空间优化。链接列表很容易正确和优化。如果你想要一个,任何一个品种,你在几分钟内建立一个,你就完成了。 (事实上,OrderedDict
使用了一个......)
与此同时,自Python 2.3以来,特别是自3.0以来,迭代器协议已成为一切的核心范例。其他语言借用Python的生成器是有原因的 - 但在大多数语言中,它们并没有提供相同的好处,因为整个语言并不是围绕它们构建的。能够对任何集合进行前向迭代 - 包括甚至不存在的“虚拟”集合 - 已经为您提供了懒惰的尾部共享缺点列表的大部分优点,而无需一个。另一方面,它缺少的主要是方便的恒定时间(变异)拼接 - 但是缺少的东西实际上很难适应迭代器范例。 (请参阅itertools
了解如何做到这一点 - 毕竟只是tee
和chain
- 以及这样做有多笨拙。)
(当然,对于更复杂的迭代协议也有优势,比如C ++使用的协议,或者更好的是,Swift。使用双向和可变与不可变迭代,双向链接列表可能更有用但是那里有权力和简单之间的权衡,很久以前Python就做出了选择。)
最后,但绝对不是最不重要的,链表,特别是cons-style尾部共享链表,是一种固有的递归数据结构。 Python的哲学是只有当你有固有的递归问题时才使用递归。 Guido认为这些很少,这就是为什么Python不做,例如尾递归消除。他不希望map
和reduce
用他的语言,但一旦他想出如何迭代地而不是递归地做,他就愿意让他们留下来。存储线性数据并不是一个固有的递归问题。树?当然,它们几乎必须是递归的。但是列表?否。