Recursion 如何创建缓存的递归类型? 开放系统 open System.Collections.Generic 将节点键入为y->比较符号y。符号 |->failwith“节点的比较无效。” Ty型= |Int |串 |tylist的元组 |节点记录 |Ty列表的并集 键入NodeDict

Recursion 如何创建缓存的递归类型? 开放系统 open System.Collections.Generic 将节点键入为y->比较符号y。符号 |->failwith“节点的比较无效。” Ty型= |Int |串 |tylist的元组 |节点记录 |Ty列表的并集 键入NodeDict,recursion,types,f#,Recursion,Types,F#,我认为您需要向表示您的类型的有区别的联合添加某种形式的显式“延迟”。如果没有显式的延迟,您将始终完全评估类型,因此没有关闭循环的可能性 类似的方法似乎奏效了: open System open System.Collections.Generic type Node<'a>(expr:'a, symbol:int) = member x.Expression = expr member x.Symbol = symbol override x.GetHas

我认为您需要向表示您的类型的有区别的联合添加某种形式的显式“延迟”。如果没有显式的延迟,您将始终完全评估类型,因此没有关闭循环的可能性

类似的方法似乎奏效了:

open System
open System.Collections.Generic

type Node<'a>(expr:'a, symbol:int) = 
    member x.Expression = expr
    member x.Symbol = symbol
    override x.GetHashCode() = symbol
    override x.Equals(y) = 
        match y with 
        | :? Node<'a> as y -> symbol = y.Symbol
        | _ -> failwith "Invalid equality for Node."

    interface IComparable with
        member x.CompareTo(y) = 
            match y with
            | :? Node<'a> as y -> compare symbol y.Symbol
            | _ -> failwith "Invalid comparison for Node."

type Ty =
    | Int
    | String
    | Tuple of Ty list
    | Rec of Node<Ty>
    | Union of Ty list

type NodeDict<'a> = Dictionary<'a,Node<'a>>

let get_nodify_tag =
    let mutable i = 0
    fun () -> i <- i+1; i

let nodify (dict: NodeDict<_>) x =
    match dict.TryGetValue x with
    | true, x -> x
    | false, _ ->
        let x' = Node(x,get_nodify_tag())
        dict.[x] <- x'
        x'

let d = Dictionary(HashIdentity.Structural)
let nodify_ty x = nodify d x

let rec int_string_stream = 
    Union 
        [
        Tuple [Int; Rec (nodify_ty (int_string_stream))]
        Tuple [String; Rec (nodify_ty (int_string_stream))]
        ]
Ty型=
|Int
|串
|tylist的元组
|节点记录
|Ty列表的并集
|懒惰的延迟
//(其余如前)
让rec int_string_stream=Delayed(Lazy.Create(fun()->
联合
[
元组[Int;Rec(nodify_ty(Int_字符串_流))]
元组[String;Rec(nodify_ty(int_String_stream))]
]))

这将意味着,当您在
Ty
上进行模式匹配时,您将始终需要检查
Delayed
,计算延迟值,然后再次进行模式匹配,但这可能是可行的

是的,这肯定会使它可行。但是当我用类似于
Tuple[Int;Rec(nodify\ty(lazy Int\u string\u stream))
的东西测试它时,发生的是每次我重新评估
Int\u string\u stream
表达式时,都会在字典中添加不同的节点。我感到惊讶的是,把
惰性
放在开头会有所不同。这是为什么?事实上,我越想它,就越觉得奇怪,它在重新评估时没有生成新的
Symbol
s(当我只发送
int\u string\u流
let绑定到REPL中时)。如果它是基于字典中的引用相等性进行比较,那么我所期望的行为是它会……呃,事实上它确实这样做了。我在测试中犯了一个错误,感到困惑,现在一切都有了意义。很抱歉。为了回答我的第一个问题,为什么写
Rec(nodifyÈty(lazy intÈu stringÈu stream))
比把lazy放在开头更糟糕,是因为当我有
Tuple[int;Rec(nodifyÈty(lazy intÈu stringÈu stream));Tuple[String;Rec(nodify_ty(lazy int_String_stream))]
,将为字典设置两个不同的引用。就是这样。你让我走上正轨,真是太棒了。
type Ty =
    | Int
    | String
    | Tuple of Ty list
    | Rec of Node<Ty>
    | Union of Ty list
    | Delayed of Lazy<Ty>

// (rest is as before)

let rec int_string_stream = Delayed(Lazy.Create(fun () ->
    Union 
        [
        Tuple [Int; Rec (nodify_ty (int_string_stream))]
        Tuple [String; Rec (nodify_ty (int_string_stream))]
        ]))