【问题标题】:Recursive crawl through a hierachy of list of various record types通过各种记录类型列表的层次结构递归爬取
【发布时间】:2020-02-01 10:33:38
【问题描述】:

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

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

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

我了解错误是 F# 中的类型推断,并且我可以找到的类型测试模式匹配示例涉及基类引用或可区分联合。我将尝试一个联合,如果这不起作用,请详细说明,但如果你们中的任何一个 F# 大师有要遵循的模式或任何输入都会很棒。

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

【问题讨论】:

  • 这对于 DU 来说绝对是一个很好的例子——你应该尽量避免 :? ... 模式匹配,因为它往往表明你没有很好地使用类型系统
  • 我目前怀疑我的问题是我对原始数据结构的实现。我怀疑我需要使用 DU 或递归类型创建原始数据结构,该数据结构提供给我可以递归调用的单个通用列表成员。将现有的 Record 类型包装到 DU 中提供了一种递归执行模式匹配的方法,但当前数据结构仅返回特定类型记录的列表。通过类型推断递归函数需要一个 DU 包装器列表。感谢您的帮助。

标签: f# tail-recursion


【解决方案1】:

要解决此问题,您需要匹配 obj 类型的内容,而不是不确定类型的值(类型参数 'a)。你可以通过添加box来做到这一点:

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

但是,我完全同意 John Palmer 的评论,即使用有区别的工会似乎是您的目的更好的选择。

【讨论】:

  • 谢谢,我快速使用了匹配确实允许第一种情况的框,但类型推断现在需要递归调用自身,只接受第一次匹配中的记录类型列表。我会尝试使用 DU。
  • @JoseGarces 我认为您可以通过添加类型注释 mapAsRequired (items:list&lt;#obj&gt;) ... 来使其工作,但我肯定会先尝试使用 DU。
  • 谢谢托马斯。我尝试简单地创建不同记录类型的 DU,并尝试通过 DU 类型列表递归。但是我无法使用子记录类型的父成员列表调用递归函数,因为它需要 DU 类型的列表。您可能有办法做到这一点,但我怀疑应该在原始数据结构中使用 DU 或递归类型。我求助于对层次结构的每个级别重复递归函数。但感谢您的帮助。也许如果我读了你的书,一个解决方案就会出现。再次感谢您的宝贵时间。
  • @JoseGarces 没有看到类型声明有点难以回答。您的递归调用必须在相同 DU 类型的值上运行...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-22
  • 2017-02-04
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 2013-02-09
相关资源
最近更新 更多