Doubly Linked List in a Purely Functional Programming Language
How does one go about doing doubly linked lists in a pure functional language? That is, something like Haskell where you're not in a Monad so you don't have mutation. Is it possible? (Singly linked list is obvi开发者_如何学Pythonously pretty easy).
In a pure functional language, a doubly-linked list is not that interesting. The idea of a doubly linked list is to be able to grab a node and go in either direction, or to be able to splice into the middle of a list. In a pure functionaly language you probably are better off with one of these two data structures:
A singly linked list with a pointer in the middle, from which you can go either left or right (a variant of Huet's "Zipper")
A finger tree, which is a mind-blowing data structure invented by Ralf Hinze and Ross Paterson.
I'm a big fan of the zipper; it's useful in a lot of situations.
There are a number of approaches.
If you don't want to mutate the doubly-linked list once you have constructed it you can just 'tie the knot' by relying on laziness.
http://www.haskell.org/haskellwiki/Tying_the_Knot
If you want a mutable doubly-linked list you need to fake references somehow -- or use real ones -- a la the trick proposed by Oleg Kiseylov and implemented here:
http://hackage.haskell.org/packages/archive/liboleg/2009.9.1/doc/html/Data-FDList.html
Interestingly, note that the former relies fundamentally upon laziness to succeed. You ultimately need mutation or laziness to tie the knot.
I would reiterate musicfan's question: "what exactly do you need this for?" As Norman Ramsey notes: if you need multi-directional traversal, then zippers are easier; if you need fast splicing, finger trees work well.
But, just to see how it looks...
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
In OCaml, for circular simply linked list you can always do something like that:
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
For doubly linked lists, I imagine it's possible to do something similar. But you have to rely on laziness and on records being friendly structures when it comes to typing. Quick and dirty cyclic doubly linked list:
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
精彩评论