F# 序列中的引导生成

cm0*_*007 3 f#

我有以下代码:

open System
open System.Linq

type Child = {
    id: Guid
    name: int
    parent: Guid
}

type Parent = {
    id: Guid
    name: int
    children: seq<Guid>
}

let makeChild name parentId =
    {
        Child.id = Guid.NewGuid()
        name = name
        parent = parentId
    }

let makeParent (group: IGrouping<int, int>) =
    let id = Guid.NewGuid()
    let children = group |> Seq.map (fun x -> makeChild x id)
    let ids = children |> Seq.map (fun x -> x.id)
    ({
        Parent.id = id
        name = group.Key
        children = ids
    }, children)

let makeAll (groups: seq<IGrouping<int, int>>) =
    let result = groups |> Seq.map (fun x -> makeParent x)
    let children = result |> Seq.map (fun x -> snd x) |> Seq.concat
    let parents = result |> Seq.map (fun x -> fst x)
    (parents, children)
Run Code Online (Sandbox Code Playgroud)

(我接受IGrouping<int, int>而不是seq<int * seq<int>>因为此代码需要与 C# 进行互操作。)

但是,当我运行以下命令时:

let parents, children = makeAll(Enumerable.Range(0, 100).GroupBy(fun x -> x % 10))
Run Code Online (Sandbox Code Playgroud)

那么没有一个children.[i].parent guid与parent.[j].children.[k] guid(i、j、k)相关。

为什么情况并非如此?我怎样才能让它变成这样呢?

scr*_*wtp 5

没有测试过,但问题似乎在于您枚举了结果 seq 两次,一次在 中let children,一次在行中let parents。由于 guid 生成具有副作用,因此每个枚举都会得到两个不同的结果。

如果您将 seq 缓存在行中let result(或通过将其转换为同一行中的数组或列表来实现它),您应该得到您正在寻找的内容:

let result = groups |> Seq.map (fun x -> makeParent x) |> Seq.cache
Run Code Online (Sandbox Code Playgroud)

功能上也是一样的makeParent。seqids也需要缓存。

像这样的“陷阱”就是为什么我发现在函数或接口的边界上使用具体的集合类型而不是 seq 更好。如果您正在寻找惰性,您可以通过使用惰性类型使其明确。