我是F#的新手。因此,也许我在这里错过了一些东西。但是,执行以下代码片段会导致StackOverflowException:
type Node = {
Position: int*int
mutable North: Node option
mutable South: Node option
mutable West: Node option
mutable East: Node option }
let initialNode = { Position = 0, 0; North = None; South = None; West = None; East = None }
let second = { Position = -1, 0; North = None; South = None; West = None; East = None }
initialNode.West <- Some second
second.East <- Some initialNode
let third = { Position = -1, -1; North = None; South = None; West = None; East = None }
second.North <- Some third
third.South <- Some second
let fourth = { Position = -1, 0; North = None; South = None; West = None; East = None }
third.East <- Some fourth
fourth.West <- Some third
let distanceMap = Map.empty |> Map.add initialNode 0
let needVisit = [ initialNode ]
let newDistanceMap =
(distanceMap, needVisit)
||> Seq.fold (fun map node ->
map |> Map.add node 1)
newDistanceMap |> Map.count
为什么?
如果将映射的键类型更改为记录类型的Position
属性(即Tuple
),则一切正常。因此,我怀疑问题通常是使用记录作为键类型,还是问题是使用具有mutable
属性的记录类型。
答案 0 :(得分:3)
问题与可变密钥并不直接相关,而是与您拥有周期(初始->秒->初始-> ...)有关。如果注释掉创建周期的行,它将起作用。
就像Alexey一样,您可以通过为您的类型自定义GetHashCode / Equality或更改数据表示形式来解决此问题。
这实际上是不可变数据类型的固有限制:您不能以这种简单的方式表示周期。如果您想在F#中使用此类图并避免过多的可变性,建议您使用其他方法来表示诸如adjacency list之类的图,有关深度分析的更多信息,请尝试搜索归纳图。
答案 1 :(得分:2)
因此,我怀疑问题通常是使用记录作为键类型,或者问题是使用具有可变属性的记录类型。
它是引用循环(严格来说,记录本身不需要mutable
属性)。要计算记录的哈希码,将使用其所有字段的哈希码。因此,要计算initialNode
的哈希码,您需要知道initialNode.West
的哈希码,其中使用second
,使用second.East
,使用{{ 1}},等等...等。
我不确定您不能自定义记录的initialNode
方法,尽管我不确定。
请注意,相等比较可能会遇到相同的麻烦(想象首先将GetHashCode()
移到末尾)。