从第一个元素列表数据结构进行功能 O(1) 追加和 O(n) 迭代

发布于 2024-10-22 02:55:14 字数 160 浏览 2 评论 0原文

我正在寻找一种支持以下操作的函数式数据结构:

  • 追加,O(1)
  • 按顺序迭代,O(n)

普通函数链表仅支持 O(n) 追加,而我可以使用普通的 LL 和然后反转它,反转操作将是 O(n) ,这也(部分)否定 O(1) cons 操作。

I'm looking for a functional data structure that supports the following operations:

  • Append, O(1)
  • In order iteration, O(n)

A normal functional linked list only supports O(n) append, while I could use a normal LL and then reverse it, the reverse operation would be O(n) also which (partially) negates the O(1) cons operation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

日裸衫吸 2024-10-29 02:55:14

您可以使用 John Hughes 的常量时间附加列表,现在它被称为 DList。表示是一个从列表到列表的函数:空列表是恒等函数;附加是组合,单例是缺点(部分应用)。在此表示中,每个枚举都会花费您 n 次分配,因此这可能不太好。

另一种方法是使用与数据结构相同的代数:

type 'a seq = Empty | Single of 'a | Append of 'a seq * 'a seq

枚举是树遍历,这将花费一些堆栈空间或需要某种拉链表示。这是一个转换为列表但使用堆栈空间的树遍历:

let to_list t =
  let rec walk t xs = match t with
  | Empty -> xs
  | Single x -> x :: xs
  | Append (t1, t2) -> walk t1 (walk t2 xs) in
  walk t []

这是相同的,但使用常量堆栈空间:

let to_list' t =
  let rec walk lefts t xs = match t with
  | Empty -> finish lefts xs
  | Single x -> finish lefts (x :: xs)
  | Append (t1, t2) -> walk (t1 :: lefts) t2 xs
  and finish lefts xs = match lefts with
  | [] -> xs
  | t::ts -> walk ts t xs in
  walk [] t []

您可以编写一个访问相同元素但实际上并不具体化列表的折叠函数;只需用更通用的东西替换 cons 和 nil :

val fold : ('a * 'b -> 'b) -> 'b -> 'a seq -> 'b

let fold f z t =
  let rec walk lefts t xs = match t with
  | Empty -> finish lefts xs
  | Single x -> finish lefts (f (x, xs))
  | Append (t1, t2) -> walk (t1 :: lefts) t2 xs
  and finish lefts xs = match lefts with
  | [] -> xs
  | t::ts -> walk ts t xs in
  walk [] t z

这就是你的线性时间、常量堆栈枚举。玩得开心!

You can use John Hughes's constant-time append lists, which seem nowadays to be called DList. The representation is a function from lists to lists: the empty list is the identity function; append is composition, and singleton is cons (partially applied). In this representation every enumeration will cost you n allocations, so that may not be so good.

The alternative is to make the same algebra as a data structure:

type 'a seq = Empty | Single of 'a | Append of 'a seq * 'a seq

Enumeration is a tree walk, which will either cost some stack space or will require some kind of zipper representation. Here's a tree walk that converts to list but uses stack space:

let to_list t =
  let rec walk t xs = match t with
  | Empty -> xs
  | Single x -> x :: xs
  | Append (t1, t2) -> walk t1 (walk t2 xs) in
  walk t []

Here's the same, but using constant stack space:

let to_list' t =
  let rec walk lefts t xs = match t with
  | Empty -> finish lefts xs
  | Single x -> finish lefts (x :: xs)
  | Append (t1, t2) -> walk (t1 :: lefts) t2 xs
  and finish lefts xs = match lefts with
  | [] -> xs
  | t::ts -> walk ts t xs in
  walk [] t []

You can write a fold function that visits the same elements but doesn't actually reify the list; just replace cons and nil with something more general:

val fold : ('a * 'b -> 'b) -> 'b -> 'a seq -> 'b

let fold f z t =
  let rec walk lefts t xs = match t with
  | Empty -> finish lefts xs
  | Single x -> finish lefts (f (x, xs))
  | Append (t1, t2) -> walk (t1 :: lefts) t2 xs
  and finish lefts xs = match lefts with
  | [] -> xs
  | t::ts -> walk ts t xs in
  walk [] t z

That's your linear-time, constant-stack enumeration. Have fun!

溺孤伤于心 2024-10-29 02:55:14

我相信你可以只使用标准函数链表:

  • 要附加元素,你可以使用 cons (即 O(1)
  • 按照元素的顺序迭代元素被插入后,可以先反转列表,
    (即 O(N)),然后遍历它,这也是 O(N) (并且 2xO(N) 仍然只是O(N))。

I believe you can just use standard functional linked list:

  • To append element, you can use cons (which is O(1))
  • To iterate elements in the order in which they were inserted, you can first reverse the list,
    (which is O(N)) and then traverse it, which is also O(N) (and 2xO(N) is still just O(N)).
白首有我共你 2024-10-29 02:55:14

差异清单怎么样?

type 'a DList = DList of ('a list -> 'a list)

module DList =
  let append (DList f) (DList g) = (DList (f << g))
  let cons x (DList f) = (DList (fun l -> x::(f l)))
  let snoc (DList f) x = (DList (fun l -> f(x::l)))
  let empty = DList id
  let ofList = List.fold snoc empty
  let toList (DList f) = f []

How about a difference list?

type 'a DList = DList of ('a list -> 'a list)

module DList =
  let append (DList f) (DList g) = (DList (f << g))
  let cons x (DList f) = (DList (fun l -> x::(f l)))
  let snoc (DList f) x = (DList (fun l -> f(x::l)))
  let empty = DList id
  let ofList = List.fold snoc empty
  let toList (DList f) = f []
胡大本事 2024-10-29 02:55:14

您可以创建一个函数式双端队列,它提供 O(1) 添加到任一端,以及 O(N) 进行任一方向的迭代。 Eric Lippert 写了一篇关于不可变双端队列的有趣版本 在他的博客上请注意,如果您环顾四周,您会发现该系列的其他部分,但这是对最终产品的解释。另请注意,通过一些调整,可以将其修改为利用 F# 可区分联合和模式匹配(尽管这取决于您)。

此版本的另一个有趣的属性,O(1) 从任一端查看、删除和添加(即 dequeueLeft、dequeueRight、dequeueLeft、dequeueRight 等,仍然是 O(N),而 O( N*N),采用双列表方法)。

You could create a functional Deque, which provides O(1) adding to either end, and O(N) for iteration in either direction. Eric Lippert wrote about an interesting version of an immutable Deque on his blog note that if you look around you will find the other parts of the series, but that is the explanation of the final product. Note also that with a bit of tweaking it can be modified to utilize F# discriminated unions and pattern matching (although that is up to you).

Another interesting property of this version, O(1) peek, removal, and add, from either end (i.e. dequeueLeft, dequeueRight, dequeueLeft, dequeueRight, etc. is still O(N), versus O(N*N) with a double list method).

笑饮青盏花 2024-10-29 02:55:14

循环链表怎么样?它支持 O(1) 追加和 O(n) 迭代。

What about a circularly-linked list? It supports O(1) appends and O(n) iteration.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文