【问题标题】:Doubly Linked List in a Purely Functional Programming Language纯函数式编程语言中的双向链表
【发布时间】:2010-12-23 02:03:22
【问题描述】:

如何在纯函数式语言中创建双向链表?也就是说,像 Haskell 这样的东西,你不在 Monad 中,所以你没有突变。可能吗? (单链表显然很简单)。

【问题讨论】:

  • 出于好奇,您到底需要这个做什么?

标签: data-structures haskell functional-programming linked-list


【解决方案1】:

在纯函数式语言中,双向链表并不那么有趣。双向链表的想法是能够抓住一个节点并朝任一方向前进,或者能够拼接到列表的中间。在纯函数式语言中,使用以下两种数据结构中的一种可能会更好:

  • 一个单链表,中间有一个指针,您可以从中向左或向右(Huet 的“Zipper”的变体)

  • 手指树,这是由 Ralf Hinze 和 Ross Paterson 发明的令人惊叹的数据结构。

我是拉链的忠实粉丝;它在很多情况下都很有用。

【讨论】:

  • 我绝对同意这些是更好的选择。 =)
  • 手指树...有趣... :)
  • Data.Sequence 是实现。手指树的,好用:)
【解决方案2】:

有多种方法。

如果您不想在构建双向链表后对其进行变异,则可以依靠懒惰来“打结”。

http://www.haskell.org/haskellwiki/Tying_the_Knot

如果您想要一个可变的双向链表,您需要以某种方式伪造引用 - 或使用真实的引用 - 这是 Oleg Kiseylov 提出并在此处实现的技巧:

http://hackage.haskell.org/packages/archive/liboleg/2009.9.1/doc/html/Data-FDList.html

有趣的是,请注意,前者从根本上依赖懒惰才能成功。你最终需要突变或懒惰来打结。

【讨论】:

    【解决方案3】:

    我想重申一下音乐迷的问题:“你到底需要这个做什么?”正如 Norman Ramsey 所说:如果您需要多向穿越,那么拉链更容易;如果您需要快速拼接,手指树效果很好。

    但是,只是看看它看起来如何......

    import Control.Arrow
    import Data.List
    
    data LNode a = LNode { here :: a, prev :: LList a, next :: LList a }
    type LList a = Maybe (LNode a)
    
    toList :: LList a -> [a]
    toList = unfoldr $ fmap $ here &&& next
    
    fromList :: [a] -> LList a
    fromList l = head nodes where
        nodes = scanr ((.) Just . uncurry LNode) Nothing $ zip l $ Nothing : nodes
    
    append :: LList a -> LList a -> LList a
    append = join Nothing where
        join k (Just a) b = a' where
            a' = Just $ a { prev = k, next = join a' (next a) b }
        join k _ (Just b) = b' where
            b' = Just $ b { prev = k, next = join b' Nothing (next b) }
        join _ _ _ = Nothing
    

    【讨论】:

      【解决方案4】:

      在 OCaml 中,对于循环简单链表,您总是可以这样做:

      type t = { a : t Lazy.t }
      
      let cycle n =
        let rec start = {a = lazy (aux n) }
        and aux = function
          | 0 -> start
          | n -> { a = lazy (aux (n-1))}
        in start
      

      对于双向链表,我想可以做类似的事情。但是在打字时,你必须依赖懒惰和记录是友好的结构。又快又脏的循环双向链表:

        type 'a t = { data : 'a; before : 'a t Lazy.t; after : 'a t Lazy.t }
      
        let of_list l =
          match l with [] -> assert false | hd::tl ->
          let rec start = { data = hd; before = last; after = next }
          and couple = lazy (aux (lazy start) hd) 
          and next = lazy (Lazy.force (fst (Lazy.force couple)))
          and last = lazy (Lazy.force (snd (Lazy.force couple)))
          and aux before = function
          | [] -> (lazy start), before
          | hd::tl -> let rec current = lazy { data = hd; before = before; after = after }
                         and couple = lazy (aux current tl) 
                         and after = lazy (Lazy.force (fst (Lazy.force couple)))
                         and last = lazy (Lazy.force (snd (Lazy.force couple))) in
                         current, last
          in start
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        • 2018-08-02
        • 1970-01-01
        • 2010-10-30
        • 2013-03-27
        • 2020-05-12
        • 1970-01-01
        相关资源
        最近更新 更多