在F#中创建(伪)循环区分联合

时间:2011-01-26 19:23:22

标签: algorithm f#

我在这里遇到了一个小问题。我写了Tortoise and Hare cycle detection algorithm

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

let next node = 
    match node 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

这对

非常有用
  let first = DataNode(1, DataNode(2, DataNode(3, DataNode(4, LastNode(5)))))
  findCycle(first)

显示错误。对。现在,当试图测试一个循环时,我无法创建循环!

显然这不会起作用:

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

但我需要那样的东西!你能告诉我如何创建一个吗?

4 个答案:

答案 0 :(得分:3)

您定义的类型不能对您的类型执行此操作。有关可行的替代方法,请参见How to create a recursive data structure value in (functional) F#?

作为Brian解决方案的替代方案,您可以尝试以下方式:

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}) }) }) })

答案 1 :(得分:1)

这是一种方式:

  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))

答案 2 :(得分:0)

即使Brian和kvb都发布了有效的答案,我仍然觉得我需要看看是否有可能以不同的方式实现同​​样的目标。此代码将为您提供包含为Seq&lt;'a&gt;

的循环结构
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; ...]

结构本身不是循环的,但它从外面看起来像。

或者你可以这样做:

//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

答案 3 :(得分:0)

  

你能告诉我如何创建吗?

在F#中有各种黑客来获取直接循环值(正如Brian和kvb所示),但我注意到这很少是你真正想要的。直接循环数据结构是一个可以调试的猪,通常用于性能,因此变得可变。

例如,您的循环图可能表示为:

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

在F#中表示图形的惯用方法是存储从句柄到顶点映射的字典,如果需要,还存储另一个用于边缘的字典。这种方法更容易调试,因为您通过可理解的查找表遍历间接递归,而不是尝试解密堆中的图形。但是,如果你想让GC收集无法访问的子图,那么弱哈希映射的纯功能替代方案显然是计算机科学中尚未解决的问题。