【发布时间】:2016-02-03 03:13:07
【问题描述】:
我的意思是 list 对其他数据结构有什么优势,这使得它在函数式语言中几乎是不可避免的?
【问题讨论】:
标签: list functional-programming
我的意思是 list 对其他数据结构有什么优势,这使得它在函数式语言中几乎是不可避免的?
【问题讨论】:
标签: list functional-programming
“没有勺子。”
如果我告诉你没有字符串这样的东西怎么办?仅存在单个字符的列表。
如果我告诉你没有清单这样的东西怎么办?只存在对。
; construct a pair of a and b
(cons 'a 'b) ; => ('a 'b)
; get the first element of the pair
(first (cons 'a 'b)) ; => 'a
; get the second element of the pair
(second (cons 'a 'b)) ; => 'b
; create a "list"
(define x (cons 'a (cons 'b (cons 'c (cons 'd (cons 'e null))))))
; => ('a ('b ('c ('d ('e ())))))
; get the third element in the "list", x
(first (second (second x)))
; => 'c
如果我告诉你没有配对这种东西怎么办?只有 lambdas 存在。
(define (cons x y)
(λ (f) (f x y)))
(define (first p)
(p (λ (x y) x)))
(define (second p)
(p (λ (x y) y)))
当然,这只是一种可能的实现方式。但重要的是要意识到这一切都只是一种幻觉。
数据抽象真的很神奇。好的语言允许您发明任何您希望使用的结构,并定义任何构造函数/选择器,使其对使用您的结构有用。有些语言只是提供比其他语言更多的语法糖。
列表很常见,因为作为程序员,我们经常处理事物的有序集合。其他常见的类型是集合和地图。只是不要自欺欺人地认为这是非常特别的东西^,^
【讨论】:
函数式语言更喜欢不变性,因此,在数据结构方面,函数式语言更喜欢persistent data structures。简而言之,持久数据结构是我们可以继续访问该结构的任何先前版本以及当前版本的数据结构。我们不会对现有的数据结构进行变异,我们只是以现有的数据结构为基础创建一个新的。
对于任何数据结构都可以简单地通过复制来完成。例如,考虑一些伪 F#:
let array1 = [| 1, 2, 3, 4, 5 |]
let array2 = append 6 array1 // [| 1, 2, 3, 4, 5, 6 |]
要实现append 函数,我们必须创建一个n+1 大小的新数组,并用array1 的副本和要附加的元素填充它。
注意这不是很有效,每个追加都需要n 副本和为整个结构分配内存。
考虑一下我们定义一些类型:
type List<'a> =
|Empty
|Cons of 'a * List<'a>
使用Cons 的情况,我们可以从旧列表和单个元素构造新列表。
假设我们现在有一个包含 100 万个元素的列表:
let list1 = [1..1000000]
let list2 = Cons (0, list1) // [0..1000000]
在这种情况下,list2 只是指list1,不需要复制。因此,添加到列表开头是一个 O(1) 操作,尽管存在数据持久性,但内存占用量仅随每个元素线性增长。
还有许多其他持久性数据结构,二叉树通常用于制作不可变的集合和字典。手指树可以作为双端队列和优先队列等结构的基础。
因此,简而言之,列表在函数式语言中被广泛使用,因为它们是一种简单、高效、持久的数据结构。
【讨论】: