F# 通过各种记录类型列表的层次结构进行递归爬网

F# 通过各种记录类型列表的层次结构进行递归爬网,f#,tail-recursion,F#,Tail Recursion,我有一个数据结构,它由一个F#记录列表组成,其中一个成员本身是一个不同类型的记录列表,依此类推到大约4个层次。我必须创建此结构的代码有点冗长,但可以正常工作。现在,我希望创建一个通用的尾部递归函数,该函数从层次结构的顶层分解此数据结构的列表,以生成层次结构的列表底层中项目数的计数映射。我可以通过创建函数来分解层次结构每个级别的记录来开发所需的代码,但最终使用相同的递归函数来处理不同记录类型的列表。以下是我如何尝试以非详细的方式实现此功能,但出现以下错误: 此运行时强制或类型测试来自类型 'a 到

我有一个数据结构,它由一个F#记录列表组成,其中一个成员本身是一个不同类型的记录列表,依此类推到大约4个层次。我必须创建此结构的代码有点冗长,但可以正常工作。现在,我希望创建一个通用的尾部递归函数,该函数从层次结构的顶层分解此数据结构的列表,以生成层次结构的列表底层中项目数的计数映射。我可以通过创建函数来分解层次结构每个级别的记录来开发所需的代码,但最终使用相同的递归函数来处理不同记录类型的列表。以下是我如何尝试以非详细的方式实现此功能,但出现以下错误:

此运行时强制或类型测试来自类型
'a


编组面板

涉及基于此程序点之前的信息的不确定类型。某些类型上不允许进行运行时类型测试

我知道错误是F#中的类型推断,我可以找到的类型测试模式匹配示例要么涉及基类引用,要么涉及有区别的并集。我将尝试一个联盟,如果它不起作用,那么就详细地说,但是如果你们中的任何一位F#gurus有模式可以遵循,或者有任何意见,那就太好了

let rec mapAsRequired items (currentCBMap: Map<string*string*string*string, int>) =
    match items with
        | head :: tail ->
            match head with
                | :? MarshallingPanel as marshallingPanel ->
                        mapAsRequired marshallingPanel.PLCs currentCBMap
                | :? PLC as plc ->
                        mapAsRequired plc.Racks currentCBMap
                | :? Rack as rack ->
                        mapAsRequired rack.Slots currentCBMap
                | _ ->
                    mapAsRequired [] currentCBMap
            mapAsRequired tail currentCBMap
        | [] ->
            currentCBMap

let rec mapMarshallingPanels (marshallingPanels:MarshallingPanel list) (currentCBMap: Map<string*string*string*string, int>) = 
    match marshallingPanels with
        | head :: tail ->
            mapMarshallingPanels tail (mapAsRequired (List.sortBy(fun (plc:PLC) -> rankProcessorForCBAlllocation plc.PLCNo) head.PLCs) currentCBMap)
        | [] ->
            currentCBMap

mapAsRequired marshallingPanels Map.empty
让rec映射为所需项目(当前CBMAP:Map)=
将项目与
|头:尾->
迎头赶上
| :? 编组面板作为编组面板->
mapAsRequired marshallingPanel.PLCs currentCBMap
| :? PLC作为PLC->
mapAsRequired plc.机架电流CBMAP
| :? 机架作为机架->
MAPAS所需机架插槽currentCBMap
| _ ->
mapAsRequired[]当前CBMAP
mapAsRequired tail currentCBMap
| [] ->
当前CBMAP
让rec映射编组面板(编组面板:编组面板列表)(当前CBMAP:Map)=
将编组面板与
|头:尾->
mapMarshallingPanels tail(mapAsRequired(List.sortBy(fun(plc:plc)->rankProcessorForCBAlllocation plc.PLCNo)head.PLCs)currentCBMap)
| [] ->
当前CBMAP
mapAsRequired marshallingPanels Map.empty

要解决此问题,您需要匹配类型为
obj
的对象,而不是不确定类型的值(类型参数
'a
)。您可以通过添加

match box head with 
| :? MarshallingPanel as marshallingPanel -> 
    mapAsRequired marshallingPanel.PLCs currentCBMap 
| :? PLC as plc -> 
    mapAsRequired plc.Racks currentCBMap 

然而,我完全同意约翰·帕尔默的评论,即使用歧视性的工会似乎是一个更好的选择

这显然是DU的一个好例子-您应该尽量避免使用
模式匹配,因为它倾向于表明您没有很好地使用类型系统。我目前怀疑我的问题是我对原始数据结构的实现。我怀疑我需要创建具有DU或递归类型的原始数据结构,该数据结构提供给我可以递归调用的单个泛型列表成员。将现有记录类型包装到DU中提供了一种递归执行模式匹配的方法,但当前数据结构只返回特定类型记录的列表。通过类型推断递归函数需要DU包装器的列表。谢谢你的帮助。谢谢,我很快就找到了一个使用框,匹配确实允许第一种情况,但是类型推断现在需要递归调用回自身,只接受第一个匹配中记录类型的列表。我将尝试使用DU。@JoseGarces我认为您可以通过添加类型注释
mapAsRequired(items:list)…
,使其正常工作,但我肯定会先尝试使用DU。谢谢Tomas。我试图简单地创建一个不同记录类型的DU,并尝试递归遍历DU类型列表。但我无法使用子记录类型的父成员列表调用递归函数,因为它需要DU类型列表。您可能有办法做到这一点,但我怀疑您应该在原始数据结构中使用DU或递归类型。我求助于为层次结构的每个级别重复递归函数。但是谢谢你的帮助。如果我读了你的书,也许会有一个解决办法。再次感谢您的时间。@JoseGarces在没有看到类型声明的情况下回答有点困难。但是,递归调用必须在相同DU类型的值上运行。。。