F# 藏东西?

F# 藏东西?,f#,F#,错误标题的Sry可以找到正确的词 目前,我正在尝试制作一些基本的数据结构,F#可以在任何情况下使用,第一个是一个双链接列表 我的问题不是如何实现它,而是为什么要隐藏数据结构的丑陋。简言之,我有一个节点看起来像 type Node<'N> = | (node<'N> ref, 'N, node<'N>) | Empty type Node ref,'N,Node您可以将F#type包装在一个类中,并隐藏实际的F#表示。例如,如果您想要一个超级简单的可

错误标题的Sry可以找到正确的词

目前,我正在尝试制作一些基本的数据结构,F#可以在任何情况下使用,第一个是一个双链接列表

我的问题不是如何实现它,而是为什么要隐藏数据结构的丑陋。简言之,我有一个节点看起来像

type Node<'N> = 
  | (node<'N> ref, 'N, node<'N>)
  | Empty
type Node ref,'N,Node您可以将F#type包装在一个类中,并隐藏实际的F#表示。例如,如果您想要一个超级简单的可变列表,您可以这样做:

type private MyListNode<'T> = 
  | Empty 
  | Cons of 'T * MyListNode<'T>

type MyList<'T>() =
  let mutable nodes = Empty
  member x.Prepend(el) = nodes <- Cons(el, nodes)
  member x.ToArray() = 
    let rec loop el = seq {
      match el with 
      | Empty -> ()
      | Cons(x, xs) -> 
          yield x
          yield! loop xs }
    loop nodes |> Array.ofSeq
键入私有MyListNode()=
让可变节点=空
成员x.Prepend(el)=节点()
|缺点(x,xs)->
产量x
产量循环xs}
循环节点|>Array.ofSeq

C#用户可以使用
MyList
,这是一个普通类,具有
Prepend
ToArray
方法。
MyListNode
类型是私有的(隐藏在您的F#库中),C#用户将永远看不到它。

这不是对问题的回答,而是对注释的回答,因为我要说的是需要图表,所以它在注释中不起作用

他写道:


但是我的双链接列表是在O(1)中运行的,如果我们假设只有数据而不是“指针”是不可变的,那么您仍然可以在O(1)时间内复制整个列表,因为您在添加或删除时所做的唯一事情是更改或生成指针(ref cell)对于旧列表,我们仍然有一个旧列表的副本,没有再次复制每个元素

如果您尝试这样做,您会发现,对于双链接列表,您实际上无法保留旧的列表指针。原因如下

使用单链接列表,您可以在O(1)时间内预先添加到列表,同时保持指向旧列表的任何指针不变。下面是一个例子:

包含三项的旧列表:

预编新标题后的新列表:

请注意,来自其他代码的引用是如何保持完整的。另一代码引用的旧列表是
[“项目1”;“项目2”;“项目3”]
。新列表为
[“新标题项目”;“项目1”;“项目2”;“项目3”]
。但是代码的另一部分所包含的引用仍然指向一个结构良好的列表。正如您将要看到的,“格式良好”的部分很重要

有了双链表,事情变得更复杂了——事实证明,不可能保持不变性并且有O(1)个时间。首先,让我们看看包含三项的旧列表:

这是一个格式良好的双链接列表。它遵守所有格式良好的双链接列表都应遵守的以下属性:

  • 所有节点都有一个前向和后向指针
  • 除head节点外,所有节点都有一个有效的(非空)返回指针。只有头部节点的后指针是
    null
  • 除尾部节点外,所有节点都具有有效(非空)Fwd指针。只有尾部节点的Fwd指针是
    null
  • 从任何不是尾部的节点开始,向前走,然后再向后走,都会使您回到开始时所在的节点
  • 从任何不是头部的节点,往回走,然后向前走,应该会使您回到开始时的同一节点
  • 现在,我们如何添加一个新的头项,同时仍然确保来自其他代码的引用继续指向格式良好的双链接列表

    这是第一次尝试。我们添加新的头项,调整其Fwd指针以指向“旧”头节点,并重写该节点的后指针以指向新的头节点:

    这仍然是一个格式良好的列表,您可以轻松验证。对于每个节点,所有五个属性仍然保持为真。但是,等等来自代码其他部分的引用已从下面的列表中更改出来!以前它指的是三个项目的列表,现在它指的是四个项目列表中的第二个项目!如果其他代码只是向前迭代,它不会注意到更改。但当它尝试向后迭代时,它会注意到有一个新的head项以前不存在!我们已经违背了不变性的承诺。不变性是对使用我们的数据结构的其他代码的一种保证,即“如果您引用了此数据结构,您看到的数据将永远不会从您下面更改。”而我们刚刚打破了这一承诺:旧代码用于查看列表
    [“项目1”;“项目2”;“项目3”]
    ,现在它看到了列表
    [“新标题项目”;“项目1”;“项目2”;“项目3”]

    好吧,那么。有没有办法遵守承诺,不改变其他代码看到的内容?好吧,我们可以尝试不重写旧的头部节点;这样旧代码仍然可以看到一个由三项组成的双链接列表,每个人都很高兴,对吧?好吧,让我们看看如果我们这样做会是什么样子:

    太棒了:另一段代码仍然看到它以前看到的完全相同的双链接列表,并且没有办法从旧列表到新的head节点。因此,其他代码的任何部分尝试从列表的head向后移动时,都会发现head仍然像应该的那样变为
    null
    。但是等等:那五个属性呢格式良好的列表的RTE?事实证明,我们违反了属性#4:从head节点开始,向前走,然后再向后走,最后到达一个空指针,而不是我们开始的节点。因此,我们不再有格式良好的列表:bummer

    好的,这样的方法是行不通的。我们还可以尝试什么。好吧……嘿!我有个主意!让我们只复制一个旧的头部节点,并在不复制旧的头部节点的情况下调整副本!这仍然是O(1),因为我们知道我们只复制一个节点。然后另一个代码会看到它以前看到的内容,一个包含三项的列表,但是