OCaml 中的 D 类不可变数据切片
Posted
技术标签:
【中文标题】OCaml 中的 D 类不可变数据切片【英文标题】:D-like slices of immutable data in OCaml 【发布时间】:2014-05-23 09:59:32 【问题描述】:OCaml 是否有切片(例如不可变数据的 D 切片)?看起来它非常适合 OCaml 范例(您可以避免每次想要使用尾递归进行任何类型的处理时都必须不断地反转列表,因为您可以从两端访问/切片列表)。实施起来会不会很困难?
举个例子,如果 OCaml 列表表现得像切片,我可以说
let merge lhs rhs =
merge_helper lhs rhs []
let rec merge_helper lhs rhs res =
match lhs with
| [] -> res ^ rhs
| l_first :: l_rest ->
match rhs with
| [] -> res ^ lhs
| r_first :: r_rest ->
if l_first <= r_first then
merge_helper l_rest rhs (res ^ [l_first])
else
merge_helper lhs r_rest (res ^ [r_first])
其中 lhs ^ rhs 尝试通过将 rhs 复制到 lhs 旁边的空间(如果可用)来连接它们,否则将它们复制到内存中至少是 lhs 两倍大的新插槽中。
编辑:也许我需要澄清一下 诸如 let concatted = lhs ^ rhs 之类的连接不是变异操作。 lhs 将与以前相同,而 rhs 将与以前相同。 concatted 可能指向或不指向与 lhs 相同的内存段(只是长度更大)。我所说的复制是一种“幕后”操作。从客户端的角度来看,所有对象的行为都好像它们是不可变的,并且构造 lhs ^ rhs 需要摊销 O(|rhs|) 时间(摊销的意义在于,如果我们通过重复连接右侧的事物来继续构造更长的切片,则数字内部重新分配的数量很少)。
编辑 2:对不起,我想象连接的行为类似于 D 附加。 D 没有这样做,因为它们也允许可变数据切片,但在 OCaml 中默认为不可变,所以这不会是一个问题(至少,不会比 D 列表更多)。
【问题讨论】:
【参考方案1】:我想你不明白什么是列表。您似乎认为列表就像一个 c++ 向量(我不知道 D 的切片,但根据我的发现,它看起来像一个 c++ 向量)但有更多限制。
不是!列表是不可变的、持久的并提供恒定的时间缺点(::),而使用数组是不可能实现这一点的(即使使用称为 slice 的智能数组)。
可以使用列表但不能使用数组的示例:
let l = [1; 2; 3; 4; 5]
let a = 0 :: l
let b = 1 :: l
最后两行是常数时间(不管l
的大小)并加上常数空间。
【讨论】:
您可以换个方向,让列表具有(摊销)双面队列的性能。有关详细信息,请参阅 Chris Okasaki 的作品。 amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/… 我知道列表是什么,但正如您所承认的,您不知道切片是什么。 D 切片是不可变的(如果类型被标记为不可变),持久并且具有摊销的常量时间附加,因此它确实符合您所说的一切。 D 切片和数组之间的最大区别在于,切片不拥有它们引用的内存,因此可以有多个切片指向重叠的内存。在得出结论之前,请先阅读 D 切片:dlang.org/d-array-article.html @dspyz 我在写答案之前已经阅读了这个页面,上面写着If [some data has already been concatenated], a new array block is allocated that will hold the existing and new data
,这不是列表的工作方式,在我的答案示例中也是不可取的。所以正如我已经说过的,D 切片没有恒定的时间缺点。如果我们喜欢列表,那正是因为它们提供了恒定的时间限制。
@Thomash 对不起,我没有仔细看你的例子。没错,D 切片不适用于您希望多个列表共享相同尾部的情况(例如制作 Collatz 序列表),但是我发现每个尾部最多具有更常见的情况一个头。在这种情况下,切片更加灵活(也更加节省时间和空间)
@dspyz 到目前为止,我只阅读了它的摘录。我也很想买。以上是关于OCaml 中的 D 类不可变数据切片的主要内容,如果未能解决你的问题,请参考以下文章