开发者

Create (pseudo) Cyclic Discriminated Unions in F#

I've run into a small problem here. I wrote the Tortoise and Hare cycle detection algorithm.

type Node = 
    | DataNode of int * Node
    | LastNode of int

let next node = 
    match no开发者_Go百科de with 
    |DataNode(_,n) -> n 
    |LastNode(_) -> failwith "Error"

let findCycle(first) =
    try
      let rec fc slow fast =
          match (slow,fast) with
          | LastNode(a),LastNode(b) when a=b -> true
          | DataNode(_,a), DataNode(_,b) when a=b -> true
          | _ -> fc (next slow) (next <| next fast)
      fc first <| next first 
    with
    | _ -> false

This is working great for

  let first = DataNode(1, DataNode(2, DataNode(3, DataNode(4, LastNode(5)))))
  findCycle(first)

It shows false. Right. Now when try to test it for a cycle, I'm unable to create a loop!

Obviously this would never work:

  let first = DataNode(1, DataNode(2, DataNode(3, DataNode(4, first))))

But I need something of that kind! Can you tell me how to create one?


You can't do this with your type as you've defined it. See How to create a recursive data structure value in (functional) F#? for some alternative approaches which would work.

As an alternative to Brian's solution, you might try something like:

type Node = 
| DataNode of int * NodeRec
| LastNode of int
and NodeRec = { node : Node }

let rec cycle = DataNode(1, { node = 
                DataNode(2, { node = 
                DataNode(3, { node = 
                DataNode(4, { node = cycle}) }) }) })


Here is one way:

  type Node = 
  | DataNode of int * Lazy<Node>
  | LastNode of int

  let next node = match node with |DataNode(_,n) -> n.Value |LastNode(_) -> failwith "Error"

  let findCycle(first) =
      try
          let rec fc slow fast =
              match (slow,fast) with
              | LastNode(a),LastNode(b) when a=b->true
              | DataNode(a,_), DataNode(b,_) when a=b -> true
              | _ -> fc (next slow) (next <| next fast)
          fc first <| next first 
      with
      | _ -> false


  let first = DataNode(1, lazy DataNode(2, lazy DataNode(3, lazy DataNode(4, lazy LastNode(5)))))
  printfn "%A" (findCycle(first))


  let rec first2 = lazy DataNode(1, lazy DataNode(2, lazy DataNode(3, lazy DataNode(4, first2))))
  printfn "%A" (findCycle(first2.Value))


Even though both Brian and kvb posted answers that work, I still felt I needed to see if it was possible to achieve the same thing in a different way. This code will give you a cyclic structure wrapped as a Seq<'a>

type Node<'a> = Empty | Node of 'a * Node<'a>

let cyclic (n:Node<_>) : _ = 
  let rn = ref n

  let rec next _ =
    match !rn with
    | Empty -> rn := n; next Unchecked.defaultof<_>
    | Node(v, x) -> rn := x; v

  Seq.initInfinite next

let nodes = Node(1, Node(2, Node(3, Empty)))
cyclic <| nodes |> Seq.take 40 // val it : seq<int> = seq [1; 2; 3; 1; ...]

The structure itself is not cyclic, but it looks like it from the outside.

Or you could do this:

//removes warning about x being recursive 
#nowarn "40"

type Node<'a> = Empty | Node of 'a * Lazy<Node<'a>>

let rec x = Node(1, lazy Node(2, lazy x))

let first = 
  match x with
  | Node(1, Lazy(Node(2,first))) -> first.Value
  | _ -> Empty


Can you tell me how to create one?

There are various hacks to get a directly cyclic value in F# (as Brian and kvb have shown) but I'd note that this is rarely what you actually want. Directly cyclic data structures are a pig to debug and are usually used for performance and, therefore, made mutable.

For example, your cyclic graph might be represented as:

> Map[1, 2; 2, 3; 3, 4; 4, 1];;
val it : Map<int,int> = map [(1, 2); (2, 3); (3, 4); (4, 1)]

The idiomatic way to represent a graph in F# is to store a dictionary that maps from handles to vertices and, if necessary, another for edges. This approach is much easier to debug because you traverse indirect recursion via lookup tables that are comprehensible as opposed to trying to decipher a graph in the heap. However, if you want to have the GC collect unreachable subgraphs for you then a purely functional alternative to a weak hash map is apparently an unsolved problem in computer science.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜